Python: Problems and Solutions (Chapter 6. Functions. Tic-Tac-Toe Game).


We continue to practice programming. After the sixth chapter in the book: Michael Dawson “Programming in Python”, 2014 (Michael Dawson “Python Programming for the Absolute Beginner”, 3rd Edition), where I learned how to use functions, it’s time to move on to practice. Let’s do our homework together!

Function refinement ask_number() / hod_number()

Task: Modify the ask_number() or hod_number() function so that it can be called with one more parameter – multiplicity (step size). Set the default stride to 1.

The original version of the function:

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

Improved version of the function:

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

Improvement of the game “Guess the number”

Challenge: Modify the Guess the Number game in Chapter 3 to use the ask_number() function, aka hod_number().

The source code of the game “Guess the 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 попыток :(')

The launch of the modified game “Guess the number” looks like this:

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

Another version of the modified function:

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

Improvement of the new version of the game “Guess the number”

Task: Modify the new version of the Guess the Number game (that you created while solving the previous problem) so that the main part of the program becomes the main() function. Don’t forget to call this function globally to start the game.

Solution:

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, чтобы выйти.')

Launching the game program:

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

Undefeated opponent in Tic Tac Toe

Problem: Write a computer_move() or comp_hod() function that makes the computer’s strategy flawless. Check if you can create an invincible unbeatable opponent.

The source code of the game “Tic-Tac-Toe”:

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, чтобы выйти')

The original comp_hod() function looks like this:

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

A win-win strategy of the computer in tic-tac-toe is possible in 4/5 cases if its computer’s move is the first. How to win at tic-tac-toe if you go second is a more interesting question, because in my program the user himself chooses whether to go first or not. If you go to Wikipedia on the game “Tic-tac-toe”, you can find all possible winning strategies in this game and the possibility of playing a draw.

It is impossible to create an absolutely win-win strategy for a computer, because strategies for both zeros and crosses are already known, which are guaranteed to lead to a draw. So winning is possible when the opponent makes a mistake.

  1. First of all, you need to check if you can win on the next move, and so go.
  2. If you can’t win, then you need to check if the opponent can win on the next move, and go there.

If the two rules described above are observed, then the general point is that you need to strive to make the first move. In my program, this is not possible, so let’s make a choice:

  • if the computer moves first (for the crosses), then you need to go to the center, and then go to such a free corner, which is located farthest from the last move of the zeros, and if you can’t go like that, then the move is chosen randomly;
  • if the computer moves second (plays for zeros), then we check where the first move was made:
    • if the crosses went to the center, then go to any corner, and if all the corners are already occupied, then go to any place;
    • if the crosses went to the corner with the first move, then first we occupy the center, and then we go to the corner opposite the first move of the crosses, if there are no such options, we go to the sides;
    • if the crosses made the first move on the sides of the field, we go to the center,
      • if further the crosses went into the corner – we go to the corner on the opposite side (cross-to-cross),
      • if further the crosses went to the opposite side, we go to any corners,
      • if the crosses went further along the side next to their first move, we go to the corner next to the crosses.

Now it remains to write this logic into our program. We use the BEST_HODI constant, where we write the best moves by priorities for the computer:

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

(*A month later, I discovered that this code does not work. What’s wrong with him? IDK)

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

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

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, чтобы выйти

How to make the artificial intelligence of our computer player even smarter?

You can use the idea of a “fork” of winnings. Add a move check to the computer to create two or more winning options on the next move. It is necessary that the computer strives to place its chip in such a way as to create surebets. And if this is not possible, you need to block such an option for the player.

The next important condition for creating an invincible computer is that the computer prefers the center to the corners.

Thus, we describe the algorithm of an unbeatable computer:

  • checking for a move to win the computer;
  • checking for the presence of a winning next move of the opponent in order to block him;
  • checking for the possibility of creating a “fork”, but if it is possible to block the future “forks” of the enemy, it is preferable to do this;
  • check center;
  • check corners;
  • check the sides;
  • in the case when you can choose any move, do it randomly.

To write this algorithm of an invincible opponent in the game “Tic-Tac-Toe” in a programming language, we first draw an if-else circuit:

I am a person “from the past”, so writing on paper is more convenient and familiar to me. In theory, this scheme is the “pseudocode” that remains to be simply transferred to Python. If I haven’t missed anything, this program will give us an “artificial intelligence” opponent that no one can beat. After I wrote down the final version of the algorithm, I got so tired of programming that I abandoned this business for a month or more.

And now I’m back! And I’m ready to take on this challenge again.

In order not to get confused, I decided to take the original version of the function 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

Let’s leave the beginning unchanged until the line for k in dostupnie_hodi(doska):

This part will remain as is:

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 

It remains for us to write all our if / else in the section:

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

Initially, we write down two main conditions:

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

Next, we write the subconditions:

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

Let’s write the conditions and blocks through if / else for the case when the computer goes first:

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

Let’s write conditions and blocks using if / else for the case when the computer moves second:

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

This is a ready-made psvdocode of an absolutely invincible opponent in tic-tac-toe. Let’s translate it into a programming language?

To make it easier to work with the code, we will enter the # sign to comment out the lines:

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

Now comments are highlighted in red, this allows you not to be distracted and write code in Python.

Since we need to use the value of the first move – who goes first? (human or computer) – then you will have to add the output of the value of perviy_hod to the output function def fishki ():

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

Now let’s swap the “if the computer moves first” and “if the computer moves second” block, since it’s easier to write the if perviy_hod condition for the #computer moves second option:

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

We also need the value of the person’s move, so let’s add the input parameter hod to the function:

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

Restoring the logic of the program:

      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  

Now we need to enter an additional value “the first move of a person”, in the existing function human_hod (doska, human) we do not have the opportunity to use such a value. You can try using the HODI variable, where moves are recorded during the game. You also need to record computer moves in the base of moves:

#компьютер ходит вторым (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

The “if the computer moves second” block looks like this:

#компьютер ходит вторым (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

In the “if the computer moves first” block, we need the value of the “opponent’s previous move” variable. Where can you get it? Probably, you can take hod, since this will be the previous move.

As soon as I finished writing the code, I ran into a syntax error when trying to run it. Somewhere there were extra spaces or tabs. I had to take the time to figure it out.

As a result, I got the following code for an invincible opponent in tic-tac-toe (which has not yet worked):

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, чтобы выйти')

And a slightly modified version:

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, чтобы выйти')

An unbeatable opponent through the Minimax algorithm

There is an alternative way to make an invincible opponent, using the so-called minimax game theory mathematical algorithm – in which each player tries to maximize gains and minimize losses. To do this, use the Utility function, which evaluates the value of each move in the tree of possible moves. To win the Utility must be positive.

(Yueyang Ying code)

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

 


This entry was posted in Python (en). Bookmark the permalink.

Leave a Reply

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