Python: non-standard functions. Game “Tic-tac-toe”.


We continue to learn the Python programming language Python. Let’s move on to chapter 6 “Functions. Tic-Tac-Toe Game” from the book: Michael Dawson “Python Programming”, 2014 (Michael Dawson “Python Programming for the Absolute Beginner”, 3rd Edition) to create our own functions and work with global variables .

How to create a function?

General view of a function in Python: function_name()

To create your own function, you need to declare it.

General form of a function declaration

def function_name(parameter1, parameter2...):
    '''Document string'''
  Function expression block

Functions can:

  • accept values via parameters,
  • return values via return.

Function parameter

— is the variable name in parentheses after the function name. At the time the function is declared, the parameter may not have a specific value. When a function is called, the parameter must be assigned a value.

Positional Function Parameters

A function can have several parameters:

def function_name(parameter1, parameter2...):

Calling this function with positional arguments:

function_name('value of parameter1',value of parameter2...)

Calling this function with named arguments (they don’t care about order!):

function_name(parameter1='value of parameter1', parameter2=value of parameter2,...)

equivalent to…

function_name(parameter2=value of parameter2, parameter1='value of parameter1',...)

Default parameters

def function_name(parameter1='value', parameter2=value...):

If no other parameter values are assigned when the function is called, the default parameter values will be displayed.

When assigning a default value to one parameter, this must be done for all other parameters in the function being declared.

Default parameters can be overridden when calling a function, and you can override them individually, and not all at once. For example, like this:

Function declaration ->

def function_name(parameter1='value', parameter2=value):

Вызов функции ->

function_name(parameter1='value of parameter1')

Function that returns values

def vozvrat():
    imya='kot'
    return imya

Illustration program:

def vozvrat():
    imya='kot'
    return imya
print('Возврат: ', vozvrat())

Running the program gives:

Возврат:  kot
>>> 

It is possible to return multiple values through a record return p1,p2,p3...

Function main()

The main code of the program can not be left in the global scope, but placed in a function that can be given any name, for example, main()

Then the program launch code will look like this:

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

Global variables

Variables inside a function are only available there. There are global variables that are declared outside of functions, while they are available inside functions too, then only for “reading”, unless otherwise specified.

Global variable – is created in the general scope.

Local variable – created inside the function.

Changing a global variable inside a function

To change global variables while inside a function, we write global

If a global variable is created g_peremennaya, then you can change it inside the function by writing: global g_peremennaya

Global Variable Shading

Since, in the general case, you cannot change the value of a global variable inside a function, but you can create a variable of the same name inside the function. This will be the shading. The new variable of the same name will only work inside the function. But this introduces confusion, it is better not to do this.

Global Constants

Variables are changed as the code progresses. And constants are variables written in capital letters (CONSTANTA), which we decided not to change. In general, it is better to use global constants than global variables.

Tic-tac-toe game

Task: write a program in which the user can play tic-tac-toe with a computer on a 3×3 field. The instructions for the game are shown first. Next comes the choice of who will go first. The game begins, the players take turns making moves. When someone wins or the game is tied, the results are printed and the program exits.

Let’s start by creating our own function that will show the user the instructions for the game:

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

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


''')

The game begins with the user choosing who makes the first move. To do this, we will write a function that asks the user the question “Do you want to be the first to make a move (play crosses)?”, To which you can answer “yes” or “no”. In the function declaration, you can not ask a specific question, you can simply write vopros, and then write any text in parentheses when calling the function.

To implement this block of code, we first declare the first general function that will ask a question and receive a yes or no answer:

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

The second part of the function assigns the type of chips to the players depending on who goes first, and will consist of the already declared function nachalo(vopros):

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

In these two functions, we use the variable names X and O, in order for the computer to use them correctly in the game, we need to additionally enter these variables at the global level. That is, to explain what X and O mean:

Globally setting variables:

X='X'
O='0'

Next, in the program, you need to make a function that will ask where the user wants to put his chip. The field will be 3×3 and each field is conditionally given its own number from 0 to 8. That is, you need to make a function where the user enters the cell number for his move.

def hod(vopros,low,high):
    otvet=None
    while otvet not in range(low,high):
        otvet=int(input(vopros))
    return otvet

Let’s make a function that generates playing fields:

  • two global constants are required: RAZMER_DOSKI and HODI
RAZMER_DOSKI=9 HODI=' '

The function that creates a new board with moves each time looks like this:

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

Now we need to make a function that will show the user a board with the result of each move:

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

We still need a function that checks where we can go. To do this, you need to make a function with iteration of all empty cells, so that all unoccupied places are added to the list, which is the “available moves”:

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

Separately, we will make a function that displays the winner. If the player who puts crosses wins, then the function will return X, if the winner played with zeros – 0, a draw is set by the global constant NICHYA='Ничья'(draw), if the game has not yet reached the end, then the function will return None. There is a way to determine victory by enumerating all possible sets of three cells in which the player is considered the winner:

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 varianti_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

The next function takes into account the user’s moves, for this you first need to understand where you can go, then the user is expected to enter a number from the available ones. The function accepts the user’s board and chip type. As a result of the function, the user makes a move:

def human_hod(doska,human):
    dostupnie=dostupnie_hodi(doska)
    hod=None
    while hod not in dostupnie:
        hod=hod('Твоя очередь ходить. Напиши номер поля 0-8: ',\
 0,RAZMER_DOSKI)
        if hod not in dostupnie:
            print('Поле занято. Напиши другой номер: ')
    print('Супер!')
    return hod

The time has come for the most difficult function for me – these are computer moves. It is necessary that the computer make “good” moves, try to win against the user. You can move at random if the computer chooses any free place on the field. But then it will be difficult for him to win.

In order not to “spoil” the original board, experienced programmers advise creating a “copy” of the board inside the function, into which we will make changes:

def comp_hod(doska,comp,human):
    doska=doska[:]

So that the computer does not go “from the bulldozer”, we take into account the following strategy:

  • “best” field – in the center;
  • if the central field is already occupied, then the corner fields are considered “best”;
  • the computer goes to the “best” of the empty seats on the field.

The best fields are set by a constant inside the function:

BEST_HODI=(4,0,2,6,8,1,3,5,7)

As a result, the whole function of computer moves is as follows:

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(hod)
            return hod
        doska[i]=HODI
    for j in dostupnie_hodi(doska):
        doska[j]=human
        if winner(doska)==human:
            print(hod)
            return hod
        doska[j]=HODI
    for k in dostupnie_hodi(doska):
        print(k)
        return k

We need one more function that will allow the next player to make a move, determining which chips made the last move and passing the move to other chips:

def next_hod(hod):
    if hod==X:
        return O
    else:
        return X

You will also need a function that will identify the winner and congratulate the user on the end of the game. This function will only be used at the very end of the game:

def pozdrav_pobeditela(pobeditel,comp,human):
    if pobeditel!=NICHYA:
        print('Собрана линия ', pobeditel)
    else:
        print(NICHYA)
    if pobeditel==comp:
        print('/n Компьютер выиграл!')
    elif pobeditel==human:
        print('Ты победил!')
    elif pobeditel==NICHYA:
        print(NICHYA)

When we have all the functions, we need to write a common main() function, which will combine all the individual functions into a single whole:

def main():
    instrukciya()
    comp,human=fishki()
    hod=X
    doska=new_doska()
    pokaz_doski(doska)
    while not winner(doska):
        if i==human:
            hod=human_hod(doska,human)
            doska[hod]=human
        else:
            hod=comp_hod(doska,comp,human)
            doska[hod]=comp
        pokaz_doski(doska)
        i=next_hod(hod)
    pobeditel=winner(doska)
    pozdrav_pobeditela(pobeditel,comp,human)

Let’s try to run the program with the game “Tic-Tac-Toe”, it will take only two lines of code to run:

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

Running the program showed that the game does not work as it should. Despite the fact that the logic of building the program is correct, there are mistakes somewhere in the functions themselves. We need to fix them.

Correction of errors in the game “Tic-tac-toe”.

Testing the game program and fixing bugs turned out to be quite a difficult task to do it “by eye”. How to find errors in a program? Probably the best way is to test the operation of each function separately. Let’s run each function and see where there are problems:

The instruction() function works:

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

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



>>> 

We run the fishki() function, and everything is fine with it:

Вы хотите быть первым, кто сделает ход (играть крестиками)?  да
Окей, ты играешь крестиками!
>>> 

In the hod(low,high) function, I decided to add a specific question, and it also ran successfully with values 0-8 hod(0.8):

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

The launch of the new_doska() function did not give any errors, but it did not produce a result either, since initially this block is empty.

After another couple of hours of wandering through the code, searching for errors, launching individual functions and groups of functions, I found that in several places the X sign was written with different characters, and in some places the cross was in quotes. Some other little things have been fixed. The final version of the program code (which worked and won against me!) Looks like this:

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

My first tic-tac-toe match with the computer looked like this:

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

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



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

   |   |  
---------

   |   |  
---------

   |   |   

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

   |   |  
---------

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

   |   |   

Мой ход: 
0

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

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

   |   |   

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

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

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

   |   |   

Мой ход: 
3

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

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

   |   |   

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

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

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

   |   |   

Мой ход: 
6

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

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

 0 |   |   

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

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

 


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

Leave a Reply

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