Python крестики нолики: Крестики-нолики на Python (текстовый вариант)

Содержание

Крестики-нолики на Python (текстовый вариант)

Сегодня перед нами стоит задача написать игру крестики-нолики на питоне. Напомним, что крестики-нолики это логическая игра для двух игроков на поле 3х3 клетки.

Для начала зададим поле. Поле у нас будет одномерным списком (list) с числами от 1 до 9. Для создания воспользуемся функцией range()

board = range(1,10)

Теперь напишем функцию, которая будет выводить наше поле в привычном формате.

def draw_board(board):
    print "-------------"
    for i in range(3):
        print "|", board[0+i*3], "|", board[1+i*3], "|", board[2+i*3], "|"
        print "-------------"

Результат выполнения данного кода

Самое время дать пользователям возможность вводить данные в нашу игру. Пишем функцию take_input

def take_input(player_token):
	valid = False
	while not valid:
		player_answer = raw_input("Куда поставим " + player_token+"? ")
		try:
			player_answer = int(player_answer)
		except:
			print "Некорректный ввод.  Вы уверены, что ввели число?"
            continue
		if player_answer >= 1 and player_answer <= 9:
			if (str(board[player_answer-1]) not in "XO"):
				board[player_answer-1] = player_token
				valid = True
			else:
				print "Эта клеточка уже занята"
		else:
			print "Некорректный ввод. Введите число от 1 до 9 чтобы походить."

Как вы видите, функция take_input принимает параметр player_token — крестик или нолик, в зависимости от того, чей сейчас ход. Нам важно ограничить выбор пользователя числами от 1 до 9. Для этого мы используем конструкции try/except и if/else, чтобы удостовериться, что выбранная клеточка не занята. Обратите внимание, что функция take_input не возвращает никакого значения, а только изменяет имеющийся список board.

Осталось написать функцию проверки игрового поля. Назовем эту функцию check_win.

def check_win(board):
	win_coord = ((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 each in win_coord:
		if board[each[0]] == board[each[1]] == board[each[2]]:
			return board[each[0]]
	return False 

Проверка результатов игры крестики-нолики достаточно распространенная задача по программированию. Как часто бывает, одно и то же задание в программировании можно решить несколькими способами. В данном случае мы просто создали кортеж (tuple) с выигрышными координатами и прошлись циклом for по нему. Если символы во всех трех заданных клетках равны — возвращаем выигрышный символ, иначе — возвращаем значение False. При этом важно помнить, что непустая строка (наш выигрышный символ) при приведении ее к логическому типу вернет True (это понадобится нам в дальнейшем).

Осталось создать функцию main, в которой мы соберем вместе все описанные функции.

def main(board):
	counter = 0
	win = False
	while not win:
		draw_board(board)
		if counter % 2 == 0:
			take_input("X")
		else:
			take_input("O")
		counter += 1
		if counter > 4:
			tmp = check_win(board)
			if tmp:
				print tmp, "выиграл!"
				win = True
				break
		if counter == 9:
			print "Ничья!"
			break
	draw_board(board)

Работа функции main предельно понятна, разве что строки 45 и 46 могут вызвать непонимание. Мы ждем когда переменная counter станет больше 4 для того, чтобы избежать заведомо ненужного вызова функции check_win (до пятого хода никто точно не может выиграть). Переменная tmp была создана опять же для того, чтобы лишний раз не вызывать функцию check_win, мы просто «запоминаем» ее значение и при необходимости используем на строке 48. Польза от такого подхода не так заметна при работе с небольшими объемами данных, но в целом подобная экономия процессорного времени — хорошая практика.

Теперь мы можем спокойно играть, запустив main(board)

 

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

# -*- coding: utf-8 -*-

 Хорошей игры!

PS. Исходный код игры крестики-нолики на Python 3 на github

Игра крестики-нолики на Python 3 — Пример написания программы с графическим интерфейсом

Очень полезно в целях изучения языка программирования написать на нем несколько простых программ. Здесь мы представим игру крестики-нолики, которая написана на Python 3 с графическим интерфейсом.

О программе

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

Внизу расположена кнопка для начала новой игры. По её нажатию игровое поле очистится и можно будет начать игру заново.

Игрок ставит крестики, а компьютер нолики. Как всегда, побеждает тот, кто первый составит линию из своих символов: по горизонтали, вертикали или диагонали.

Библиотеки и объявление переменных

Для отображения графики будем использовать стандартную библиотеку Tkinter, которая устанавливается вместе с Python. Так же нам потребуется библиотека random для того, чтобы получать случайные числа, благодаря которым ходы компьютера будут неожиданными.

Это также стандартная библиотека Python. Так что ничего дополнительно устанавливать не надо. Просто подключаем их с помощью import. Создаем окно root, устанавливаем ему заголовок и объявляем необходимые переменные:

  • game_run – в эту переменную будем записывать False при завершении игры, чтобы запретить делать ходы когда уже выявлен победитель.
  • field – это будет двумерный список, в котором будут храниться кнопки игрового поля. Ходом будет изменение надписи на кнопке на символ «X» или «O».
  • cross_count в этой переменной мы будем отслеживать количество крестиков на поле. Чтобы по выставлению пятого крестика, в случае если никто не выиграл фиксировать ничью.
from tkinter import *
import random
root = Tk()
root.title('Criss-cross')
game_run = True
field = []
cross_count = 0

Обработка нажатия кнопок

Функция new_game будет вызываться при нажатии кнопки начала новой игры. На поле убираются все крестики и нолики. Цвет кнопок делаем бледно-лиловым. Устанавливаем глобальные переменные game_run и cross_count в начальные значения. Это глобальные переменные к которым пытаемся обратиться из функции. Поэтому перед попыткой изменить их значение, в Python надо использовать ключевое слово global.

def new_game():
    for row in range(3):
        for col in range(3):
            field[row][col]['text'] = ' '
            field[row][col]['background'] = 'lavender'
    global game_run
    game_run = True
    global cross_count
    cross_count = 0

Функция click будет вызываться после нажатия на поле, то есть при попытки поставить крестик. Если игра еще не завершена, то крестик ставится. После этого увеличиваем счетчик количества выставленных крестиков.

Потом проверяем с помощью функции check_win, не победили ли мы этим ходом. Если еще не выявлен победитель и есть еще ходы, то выполняет ход компьютер функцией computer_move, и также после хода идет проверка выигрыша.

def click(row, col):
    if game_run and field[row][col]['text'] == ' ':
        field[row][col]['text'] = 'X'
        global cross_count
        cross_count += 1
        check_win('X')
        if game_run and cross_count < 5:
            computer_move()
            check_win('O')

Проверка победы

Функция check_win осуществляет проверку выигрыша. Она перебирает все возможные комбинации полей, образующих линию и вызывает с ними функцию check_line. Переменная smb – это символ «X» или «O», то есть крестики или нолики. Если задан «O», то проверяется: не победил ли компьютер.

Если зафиксирован выигрыш, то меняем цвет фона кнопок, составляющих линию на розовый. А также записываем в game_run значение False.

def check_win(smb):
    for n in range(3):
        check_line(field[n][0], field[n][1], field[n][2], smb)
        check_line(field[0][n], field[1][n], field[2][n], smb)
    check_line(field[0][0], field[1][1], field[2][2], smb)
    check_line(field[2][0], field[1][1], field[0][2], smb)

def check_line(a1,a2,a3,smb):
    if a1['text'] == smb and a2['text'] == smb and a3['text'] == smb:
        a1['background'] = a2['background'] = a3['background'] = 'pink'
        global game_run
        game_run = False

Проверяем все возможные варианты, так как теоретически можно одним ходом составить сразу 2 линии.

Действия компьютера

Ход компьютера рассчитывается в функции computer_move. Алгоритм его действий следующий:

  1. Проверка возможности победы. Если компьютеру представился шанс победы – он не должен его упустить. Сразу же делает победу.
  2. Проверка возможной победы противника за один ход. Если игрок выставил два крестика в ряд, компьютер пытается разрушить планы игрока.
  3. Случайный ход. Так как победить нет возможности и нет угрозы проигрыша, то выбирается случайное свободное поле. В бесконечном цикле wile перебираются случайные числа, пока они не выпадут на не занятое поле.
def can_win(a1,a2,a3,smb):
    res = False
    if a1['text'] == smb and a2['text'] == smb and a3['text'] == ' ':
        a3['text'] = 'O'
        res = True
    if a1['text'] == smb and a2['text'] == ' ' and a3['text'] == smb:
        a2['text'] = 'O'
        res = True
    if a1['text'] == ' ' and a2['text'] == smb and a3['text'] == smb:
        a1['text'] = 'O'
        res = True
    return res

def computer_move():
    for n in range(3):
        if can_win(field[n][0], field[n][1], field[n][2], 'O'):
            return
        if can_win(field[0][n], field[1][n], field[2][n], 'O'):
            return
    if can_win(field[0][0], field[1][1], field[2][2], 'O'):
        return
    if can_win(field[2][0], field[1][1], field[0][2], 'O'):
        return
    for n in range(3):
        if can_win(field[n][0], field[n][1], field[n][2], 'X'):
            return
        if can_win(field[0][n], field[1][n], field[2][n], 'X'):
            return
    if can_win(field[0][0], field[1][1], field[2][2], 'X'):
        return
    if can_win(field[2][0], field[1][1], field[0][2], 'X'):
        return
    while True:
        row = random.randint(0, 2)
        col = random.randint(0, 2)
        if field[row][col]['text'] == ' ':
            field[row][col]['text'] = 'O'
            break

Графический интерфейс

Все элементы графического интерфейса мы будем размещать с помощью упаковщика grid. В цикле добавим кнопки игрового поля. Они будут храниться в двумерном список. В языке программирования Python добавляют элементы в список с помощью метода append.

Свойство colorspan у кнопки начала игры выставляем в 3, чтобы он занимал всю ширину таблицы

for row in range(3):
    line = []
    for col in range(3):
        button = Button(root, text=' ', width=4, height=2, 
                        font=('Verdana', 20, 'bold'),
                        background='lavender',
                        command=lambda row=row, col=col: click(row,col))
        button.grid(row=row, column=col, sticky='nsew')
        line.append(button)
    field.append(line)
new_button = Button(root, text='new game', command=new_game)
new_button.grid(row=3, column=0, columnspan=3, sticky='nsew')
root.mainloop()

Игровое поле мы делали таким же образом как и кнопки в примере с калькулятором, размещённом в отдельной статье.

В целом, реализация игры крестики-нолики неплохо подходит для изучающих программирование на Python 3. Можно немного усложнить задачу, добавив уровни сложности. Например, на простом уровне сложности компьютер делает абсолютно случайные ходы. На более сложном не упускает возможности победить, но все еще может прозевать два крестика выставленных в ряд.

Python игра в крестики-нолики — CodeRoad

Я не уверен, что весь код будет необходим или нет, поэтому я опубликую его:

# Tic-Tac-Toe
# Plays the game of tic-tac-toe against a human opponent

# global constants
X = "X"
O = "O"
EMPTY = " "
TIE = "TIE"
NUM_SQUARES = 9


def display_instruct():
    """Display game instructions."""  
    print(
    """
    Welcome to the greatest intellectual challenge of all time: Tic-Tac-Toe.  
    This will be a showdown between your human brain and my silicon processor.  

    You will make your move known by entering a number, 0 - 8.  The number 
    will correspond to the board position as illustrated:

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

    Prepare yourself, human.  The ultimate battle is about to begin. \n
    """
    )


def ask_yes_no(question):
    """Ask a yes or no question."""
    response = None
    while response not in ("y", "n"):
        response = input(question).lower()
    return response


def ask_number(question, low, high):
    """Ask for a number within a range."""
    response = None
    while response not in range(low, high):
        response = int(input(question))
    return response


def pieces():
    """Determine if player or computer goes first."""
    go_first = ask_yes_no("Do you require the first move? (y/n): ")
    if go_first == "y":
        print("\nThen take the first move.  You will need it.")
        human = X
        computer = O
    else:
        print("\nYour bravery will be your undoing... I will go first.")
        computer = X
        human = O
    return computer, human


def new_board():
    """Create new game board."""
    board = []
    for square in range(NUM_SQUARES):
        board.append(EMPTY)
    return board



def display_board(board):
    """Display game board on screen."""
    print("\n\t", board[0], "|", board[1], "|", board[2])
    print("\t","---------")
    print("\t",board[3], "|", board[4], "|", board[5])
    print("\t","---------")
    print("\t",board[6], "|", board[7], "|", board[8])

def legal_moves(board):
    """Create list of legal moves."""
    moves = []
    for square in range(NUM_SQUARES):
        if board[square] == EMPTY:
            moves.append(square)
    return moves


def winner(board):
    """Determine the game winner."""
    WAYS_TO_WIN = ((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 row in WAYS_TO_WIN:
        if board[row[0]] == board[row[1]] == board[row[2]] != EMPTY:
            winner = board[row[0]]
            return winner

    if EMPTY not in board:
        return TIE

    return None


def human_move(board, human):
    """Get human move."""  
    legal = legal_moves(board)
    move = None
    while move not in legal:
        move = ask_number("Where will you move? (0 - 8):", 0, NUM_SQUARES)
        if move not in legal:
            print("\nThat square is already occupied, foolish human.  Choose another.\n")
    print("Fine...")
    return move


def computer_move(board, computer, human):
    """Make computer move."""
    # make a copy to work with since function will be changing list
    board = board[:]
    # the best positions to have, in order
    BEST_MOVES = (4, 0, 2, 6, 8, 1, 3, 5, 7)

    print("I shall take square number,", end="")

    # if computer can win, take that move
    for move in legal_moves(board):
        board[move] = computer
        if winner(board) == computer:
            print(move)
            return move
        # done checking this move, undo it
        board[move] = EMPTY

    # if human can win, block that move
    for move in legal_moves(board):
        board[move] = human
        if winner(board) == human:
            print(move)
            return move
        # done checkin this move, undo it
        board[move] = EMPTY

    # since no one can win on next move, pick best open square
    for move in BEST_MOVES:
        if move in legal_moves(board):
            print(move)
            return move


def next_turn(turn):
    """Switch turns."""
    if turn == X:
        return O
    else:
        return X


def congrat_winner(the_winner, computer, human):
    """Congratulate the winner."""
    if the_winner != TIE:
        print(the_winner, "won!\n")
    else:
        print("It's a tie!\n")

    if the_winner == computer:
        print("As I predicted, human, I am triumphant once more.  \n" \
              "Proof that computers are superior to humans in all regards.")

    elif the_winner == human:
        print("No, no!  It cannot be!  Somehow you tricked me, human. \n" \
              "But never again!  I, the computer, so swear it!")

    elif the_winner == TIE:
        print("You were most lucky, human, and somehow managed to tie me.  \n" \
              "Celebrate today... for this is the best you will ever achieve.")


def main():
    display_instruct()
    computer, human = pieces()
    turn = X
    board = new_board()
    display_board(board)

    while not winner(board):
        if turn == human:
            move = human_move(board, human)
            board[move] = human
        else:
            move = computer_move(board, computer, human)
            board[move] = computer
        display_board(board)
        turn = next_turn(turn)

    the_winner = winner(board)
    congrat_winner(the_winner, computer, human)


# start the program
main()
input("\n\nPress the enter key to quit.")

Это пример из книги, которую я читаю, и я не совсем понимаю, я думаю, что понимаю все это до тех пор, пока:

for row in WAYS_TO_WIN:
            if board[row[0]] == board[row[1]] == board[row[2]] != EMPTY:
                winner = board[row[0]]
                return winner

Может ли кто-нибудь объяснить, что делает эта функция и, более конкретно, что такое условие
if board[row[0]] == board[row[1]] == board[row[2]] != EMPTY: -это тестирование?

python

function

if-statement

conditional

Поделиться

Источник


user3295015    

09 марта 2014 в 17:57

5 ответов


  • Игра в крестики-нолики (Java): поиск игры в галстук

    Делая игру в крестики-нолики для моего класса, я правильно использую все остальные методы, и игра работает, если нет ничьей. доска-это массив 2D, представляющий собой доску tic tac toe. Вот метод Full(), чтобы попробовать и посмотреть, заполнена ли доска: public boolean full() { boolean full =…

  • Такие крестики нолики

    В C#, году у меня есть игра в крестики-нолики, в которой у меня есть метод проверки результата, если игрок выиграл, проиграл или это была ничья. У меня проблема с кодом, проверяющим, является ли это галстук. Так что прямо сейчас у меня есть это. И я хочу, чтобы это было так, если есть пробел в…



2

Это просто проверка текущей доски, чтобы увидеть, имеет ли какая-либо выигрышная комбинация ячеек (как указано в массиве строк) (а) одинаковое значение и (б) это значение не EMPTY.

Примечание: в Python, если a == b == c != d, проверяется, что a ==b AND b == c AND c != d

Таким образом, если все ячейки 0, 1 и 2 имеют X, то при первом прохождении цикла он вернет X из процедуры winner.

Поделиться


mackworth    

09 марта 2014 в 18:00



1

Лучший способ увидеть, что происходит, — это поместить некоторые операторы print в этот код при его запуске.

Судя по тому, как обстоят дела, вы можете сказать, что хотите узнать, выиграл ли кто-то в игре. Из правил TicTacToe вы знаете, что если у X или O есть три в строке, столбце или диагонали, этот игрок выигрывает. Вы видите в board[x] == board[y] == board[z] , что мы, вероятно, тестируем здесь три подряд. Так что же такое x, y z ? Ну, посмотрите на WAYS_TO_WIN . В этом массиве находятся строки, указывающие индексы, которые находятся в строке, столбце или диагонали. Таким образом, мы проверяем, содержит ли строка, столбец или диагональ один и тот же символ, и этот символ-NOT EMPTY (который является символом " " [пробел]).

Поделиться


Isaac    

09 марта 2014 в 18:04



1

Я новичок в Python. Приведенный ниже сценарий игры в крестики-нолики взят из одного из моих упражнений. Он использует другой подход.

Для структуры данных я использовал целочисленное значение 0 для пустых ячеек, +1 для ячеек, размещенных компьютером, и -1 для ячеек, размещенных пользователем.

Главное преимущество заключается в том , что я могу использовать lineValue , то есть сумму значений всех трех ячеек в строке, для отслеживания состояния каждой строки. Все 8 значений строк хранятся в списке lineValues . Это может значительно облегчить принятие решения. Например, когда наступает моя (компьютерная) очередь, если есть строка с lineValue==2, я знаю, что выиграю. В противном случае, если есть линии с lineValue ==-2, я должен заблокировать пересечение(если таковое имеется) этих линий.

Ключом к принятию решения является функция findMostValuableCell . Что он делает, так это выясняет, какая ячейка наиболее ценна для следующего хода (т. Е. Какая ячейка появляется в большинстве строк для определенного lineValue). В этом скрипте нет пробного теста (тест «что, если»). Он использует довольно много вариантов понимания списка.

Надеюсь, это поможет.

ttt = [0 for i in range(9)]
lines = [[0, 1, 2],[3, 4, 5],[6, 7, 8],[0, 3, 6],[1, 4, 7],[2, 5, 8],[0, 4, 8],[2, 4, 6]]
lineValues = [0 for i in range(8)]
userChar = {1: "O", -1: "X", 0: "_"}
turn = -1 # defalut to user move first

#*****************************************************
def main():
    global userChar, turn
    if input("Do you want me to start first? (Y/N)").lower()=="y":
        userChar = {1:"X",-1:"O",0:"_"}
        turn = 1
    display()
    while not hasWinner():
        if 0 in ttt:
            nextMove(turn)
            turn *= -1
            display()
        else:
            print("It's a tie!")
            break
#*****************************************************
def hasWinner():
    if max(lineValues) == 3:
        print("********  I win!!  ********")
        return True
    elif min(lineValues) == -3:
        print("********  You win  ********")
        return True
#*****************************************************
def nextMove(turn):
    if turn== -1: #User's turn
        print("It's your turn now (" + userChar[-1]+"):")
        while not isUserMoveSuccessful(input("Please choose your cell number:")):
            print("Your choice is not valid!")
    else: #Computer's turn
        print("It's my turn now...")
        for lineValue in [2,-2,-1,1,0]:
            cell = findMostValuableCell(lineValue)
            if cell>=0: #found a cell for placement
                markCell(cell, turn)
                print ("I chose cell", str(cell),"." )
                return
#*****************************************************
def isUserMoveSuccessful(userInput):
    s = list(userInput)[0]
    if '012345678'.find(s)>=0 and ttt[int(s)]==0:
        markCell(int(s), turn)
        return True
#*****************************************************
def findMostValuableCell(lineValue):
    if set(ttt)=={0}:
        return 1
    allLines = [i for i in range(8) if lineValues[i]==lineValue]
    allCells =[j for line in allLines for j in lines[line] if ttt[j]==0]
    cellFrequency = dict((c, allCells.count(c)) for c in set(allCells))
    if len(cellFrequency)>0: # get the cell with highest frequency.
        return max(cellFrequency, key=cellFrequency.get)
    else:
        return -1
#*****************************************************
def markCell(cell, trun):
    global lineValues, ttt
    ttt[cell]=turn
    lineValues = [sum(cellValue) for line in lines for cellValue in [[ttt[j] for j in line]]]
#*****************************************************
def display():
    print(' _ _ _\n'+''.join('|'+userChar[ttt[i]]+('|\n' if i%3==2 else '') for i in range(9)))
#*****************************************************
main()

Поделиться


Scormer    

13 мая 2016 в 19:20


  • Игра в крестики-нолики на Python с использованием TURTLE

    Я хотел бы сделать крестики-нолики, используя черепаху на python. Предположим, что я делаю сетку, делая 9 квадратов, используя следующий код: for i in range(4): turtle.forward(60) turtle.left(90) Можно ли нажать на один из этих квадратов и вернуть или сохранить позицию? Можно ли сделать игру в…

  • python «крестики-нолики» выигрышный условия

    Я кодирую логику игры в крестики-нолики. Я уже проверил все условия выигрыша в крестики-нолики. Теперь мне нужно проверить, является ли игра ничьей. board_values = [[x, x, x], [None, None, None], [None, None, None]] #the if statement for that winning condition would be if board_values[0][0]==’x’…



0

Я бы выразился проще.
Строка-это переменная, которая назначается каждому кортежу в кортеже WAYS_TO_WIN.
На первой итерации строка = (0,1,2)
он проверяет, имеет ли значение 0==1==2.

На второй итерации строка = (3,4,5)
он проверяет, имеет ли значение 3==4==5.

Строка переходит к каждому внутреннему кортежу внешнего кортежа ways_to_win, пока не будет достигнута строка = (2,4,6).
Это то, что делает программа.

Поделиться


Ashwani Jha    

14 апреля 2017 в 20:31



0

  1. Элемент списка

    деф tic_tac_toe():
    доска = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    конец = ложь
    win_commbinations = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))

    деф draw():
    печать (board[0], board[1], board[2])
    печать (board[3], board[4], board[5])
    печать (board[6], board[7], board[8])
    print()

    деф p1():
    Н = choose_number()
    если board[n] == «X» или board[n] == «O»:
    print («\nYou не может туда попасть. Пробовать снова»)
    p1()
    ещё:
    board[n] = «X»

    деф p2():
    Н = choose_number()
    если board[n] == «X» или board[n] == «O»:
    print («\nYou не может туда попасть. Пробовать снова»)
    p2()
    ещё:
    board[n] = «O»

    деф choose_number():
    пока правда:
    пока правда:
    а = input()
    пробовать:
    a = int(a)
    a — = 1
    , Если a в диапазоне (0, 9):
    вернуть
    еще:
    print («\nThat нет на доске. Пробовать снова»)
    продолжайте
    , кроме ValueError:
    print («\nThat-это не число. Пробовать снова»)
    продолжить

    деф check_board():
    количество = 0
    для a в win_commbinations:
    если доска[a[0]] == доска[a[1]] == доска[a[2]] == «X»:
    print («Игрок 1 выигрывает!\n»)
    печать («Congratulations!\n»)
    вернет true

        if board[a[0]] == board[a[1]] == board[a[2]] == "O":
            print("Player 2 Wins!\n")
            print("Congratulations!\n")
            return True
    for a in range(9):
        if board[a] == "X" or board[a] == "O":
            count += 1
        if count == 9:
            print("The game ends in a Tie\n")
            return True
    

    пока не кончится:
    draw()
    конец = check_board()
    если конец == правда:
    break
    print («Игрок 1 выбирает, где поставить крестик»)
    p1()
    print()
    draw()
    конец = check_board()
    если конец == правда:
    break
    print («игрок 2 выбирает, где разместить ноль»)
    p2()
    print()

    если входной сигнал(«снова играть (y/n)\n») == «y»:
    print()
    tic_tac_toe()

Поделиться


vamshi mannem    

30 августа 2019 в 18:55


Похожие вопросы:

Шаблон Проектирования «Крестики-Нолики»

Мне было интересно, могу ли я получить ваши мысли и советы относительно того, что было бы лучшим/наиболее выгодным шаблоном дизайна для сетевой игры в крестики-нолики? Я рассматривал следующие…

Python игра в крестики-нолики

Мне нужна помощь в том, чтобы позволить этой функции остановить людей, переопределяющих координаты, которые другой игрок уже ввел в свою игру в крестики-нолики def CheckValidMove(XCoordinate,…

Python 3.2 Переигровка Игры В Крестики-Нолики?

я запрограммировал игру в крестики-нолики в python году, и она прекрасно работает и все такое. поэтому мне было интересно, можете ли вы каким-то образом запрограммировать игру, чтобы сказать Do you…

Игра в крестики-нолики (Java): поиск игры в галстук

Делая игру в крестики-нолики для моего класса, я правильно использую все остальные методы, и игра работает, если нет ничьей. доска-это массив 2D, представляющий собой доску tic tac toe. Вот метод…

Такие крестики нолики

В C#, году у меня есть игра в крестики-нолики, в которой у меня есть метод проверки результата, если игрок выиграл, проиграл или это была ничья. У меня проблема с кодом, проверяющим, является ли это…

Игра в крестики-нолики на Python с использованием TURTLE

Я хотел бы сделать крестики-нолики, используя черепаху на python. Предположим, что я делаю сетку, делая 9 квадратов, используя следующий код: for i in range(4): turtle.forward(60) turtle.left(90)…

python «крестики-нолики» выигрышный условия

Я кодирую логику игры в крестики-нолики. Я уже проверил все условия выигрыша в крестики-нолики. Теперь мне нужно проверить, является ли игра ничьей. board_values = [[x, x, x], [None, None, None],…

игра в крестики-нолики в angular

Я работаю над игрой в крестики-нолики в Angular году, и я нашел это РЕПО github, в котором эта игра уже встроена, и она работает нормально. После запуска npm install и npm start и если я перейду к…

Python Не Выходя Из Игры В Крестики-Нолики

У меня есть очень простая игра в крестики-нолики в python. По какой-то причине игра не завершается, когда пользователь вводит e. Почему это? import os import sys os.system(cls) one = 1 two = 2 three…

Python крестики-нолики — CodeRoad

Я пытаюсь сделать игру в крестики-нолики, следуя умному руководству программиста на youtube. До этого момента все идет хорошо, как и на учебнике:

board = ['-','-','-',
         '-','-','-',
         '-','-','-',]

def display_board():
    print(board[0] + ' | ' + board[1]  + ' | ' +board[2])    
    print(board[3] + ' | ' + board[4]  + ' | ' +board[5])  
    print(board[6] + ' | ' + board[7]  + ' | ' +board[8])  


def play_game():
    # Display initial board
    display_board()


    handle_turn()


def handle_turn():
    position = input("Choose a position from 1-9: ")
    postion = int(position) - 1

    board[position] =  "X"
    display_board()



play_game()

Да, так что в основном, когда он запускает его, он может выбрать число от 1 до 9, и один из минусов на борту меняется на «X».
Когда я это делаю, у меня возникает проблема, и консоль говорит:

"TypeError: list indices must be integers or slices, not str".

Вы не могли бы мне помочь? Я не знаю, важно ли это, потому что я действительно Новичок, но он использует python 3.6.1, а я использую 3.7

python

Поделиться

Источник


Michał Pawłowski    

23 мая 2020 в 17:32

2 ответа


  • Python 3.2 Переигровка Игры В Крестики-Нолики?

    я запрограммировал игру в крестики-нолики в python году, и она прекрасно работает и все такое. поэтому мне было интересно, можете ли вы каким-то образом запрограммировать игру, чтобы сказать Do you wish to play again? после победы компьютера или если вы выиграете? в моей игре, как только…

  • Крестики-нолики переместить компьютер

    это код компьютерного хода в игре крестики нолики в python: def computermove(board,computer,human): movecom=» rmoves=rd(0,8) for movecom in legalmoves(board): board[movecom]=computer if winner(board)==computer: return movecom board[movecom]=» for movecom in legalmoves(board):…



0

У вас есть опечатка в handle_turn. Измените его на:

def handle_turn():
    position = input("Choose a position from 1-9: ")
    position = int(position) - 1

    board[position] =  "X"
    display_board()

Поделиться


CanciuCostin    

23 мая 2020 в 17:39



0

В опубликованном фрагменте кода есть опечатка . Ниже находится строка, где переменная позиция написана с ошибкой.

Неверная строка кода

postion = int(position) - 1

Исправленный фрагмент кода

board = ['-','-','-',
         '-','-','-',
         '-','-','-',]

def display_board():
    print(board[0] + ' | ' + board[1]  + ' | ' +board[2])
    print(board[3] + ' | ' + board[4]  + ' | ' +board[5])
    print(board[6] + ' | ' + board[7]  + ' | ' +board[8])

def handle_turn():
    position = input("Choose a position from 1-9: ")
    position = int(position) - 1

    board[position] =  "X"
    display_board()

def play_game():
    # Display initial board
    display_board()
    handle_turn()

play_game()

Этот код будет работать нормально.

Поделиться


satilog    

23 мая 2020 в 17:43


Похожие вопросы:

Твист на крестики нолики

я пишу программу крестики-нолики, но это не ваши традиционные крестики-нолики Во-первых, доска 4х4, и способ выиграть состоит в том, чтобы получить 3 вида и 1 из ваших противников в ряд, столбец или…

Шаблон Проектирования «Крестики-Нолики»

Мне было интересно, могу ли я получить ваши мысли и советы относительно того, что было бы лучшим/наиболее выгодным шаблоном дизайна для сетевой игры в крестики-нолики? Я рассматривал следующие…

Python игра в крестики-нолики

Мне нужна помощь в том, чтобы позволить этой функции остановить людей, переопределяющих координаты, которые другой игрок уже ввел в свою игру в крестики-нолики def CheckValidMove(XCoordinate,…

Python 3.2 Переигровка Игры В Крестики-Нолики?

я запрограммировал игру в крестики-нолики в python году, и она прекрасно работает и все такое. поэтому мне было интересно, можете ли вы каким-то образом запрограммировать игру, чтобы сказать Do you…

Крестики-нолики переместить компьютер

это код компьютерного хода в игре крестики нолики в python: def computermove(board,computer,human): movecom=» rmoves=rd(0,8) for movecom in legalmoves(board): board[movecom]=computer if…

Игра в крестики-нолики на Python с использованием TURTLE

Я хотел бы сделать крестики-нолики, используя черепаху на python. Предположим, что я делаю сетку, делая 9 квадратов, используя следующий код: for i in range(4): turtle.forward(60) turtle.left(90)…

Алгоритм поиска победителя в крестики-нолики

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

python «крестики-нолики» выигрышный условия

Я кодирую логику игры в крестики-нолики. Я уже проверил все условия выигрыша в крестики-нолики. Теперь мне нужно проверить, является ли игра ничьей. board_values = [[x, x, x], [None, None, None],…

Python определение победителя в крестики-нолики

Я делаю игру в крестики-нолики в python году в рамках своего курса в колледже. Размер платы определяется пользователем, поэтому может быть 3х3 или 15х15, что зависит от выбора пользователя. Есть ли…

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

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

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

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

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

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

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

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

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

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

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

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

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

import random 

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

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

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

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

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

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

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

Решение:

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

   |   |  
---------

   |   |  
---------

   |   |   

Мой ход: 
4

   |   |  
---------

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

   |   |   

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

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

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

   |   |   

Мой ход: 
2

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

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

   |   |   

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

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

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

   |   | 0 

Мой ход: 
6

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

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

 X |   | 0 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

for k in dostupnie_hodi(doska):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

(код Yueyang Ying)

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


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

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

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

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

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

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

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

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

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


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

    if print_game:
        game.print_board_nums()

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

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

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

        time.sleep(.8)

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



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


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

    def get_move(self, game):
        pass


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

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


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

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


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

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

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

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

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

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

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

 

python — Проблема в игре «Крестики Нолики»

Закрыт. Этот вопрос не по теме. Ответы на него в данный момент не принимаются.



Хотите улучшить этот вопрос? Обновите вопрос так, чтобы он вписывался в тематику Stack Overflow на русском.

Закрыт 4 месяца назад.


Запуская игру, я ввожу X или O, потом вводятся только мои буквы, а ИИ нет и когда я выигрываю выскакивают эти ошибки:

line 54, in getBoardCopy
return BoardCopy NameError: name ‘BoardCopy’ is not defined

line 91, in getComputerMove
boardCopy = getBoardCopy(board)

line 156, in
move = getComputerMove(theBoard, computerLetter)

Можете скопировать код и запустить игру отдельно, чтобы увидеть ошибку.

# Крестики-нолики

import random

def drawBoard(board):
    # Эта функция выводит на экран игровое поле, клетки которого будут заполняться.
    
    # "board" - это список из 10 строк, для прорисовки игрового поля (индекс 0 игнорируется).
    print(board[7] + '|' + board[8] + '|' + board[9])
    print('-+-+-')
    print(board[4] + '|' + board[5] + '|' + board[6])
    print('-+-+-')
    print(board[1] + '|' + board[2] + '|' + board[3])
def inputPlayerLetter():
    # Рзарешение игроку ввести букву, которую он выбирает.
    # Возвращает список, в котором буква игрока - первый элемент, а буква компьютера - второй.
    letter = ''
    while not(letter == 'X' or letter == 'O'):
        print('Вы выбираете X или O?')
        letter = input().upper()
    # Первым элементом списка является бувка игрока, вторым - буква компьютера.
    if letter == 'X':
        return ['X','O']
    else:
        return ['O', 'X']

def whoGoesFirst():
    # Случайным выбор игрока, который ходит первым.
    if random.randint(0,1) == 0:
        return 'Компьютер'
    else:
        return 'Человек'
    
def makeMove(board, letter, move):
    board[move] = letter
    
def isWinner(bo, le):
    # Учитывая заполнение игрового поля и буквы игрока, эта функция возвращает True, если игрок выиграл.
    # Мы используем "bo" вместо "board" и "le" вместо "letter", поэтому нам не нужно иного печатать.
    return ((bo[7] == le and bo[8] == le and bo[9] == le) or
    (bo[4] == le and bo[5] == le and bo[6] == le) or
    (bo[1] == le and bo[2] == le and bo[3] == le) or
    (bo[7] == le and bo[4] == le and bo[1] == le) or
    (bo[8] == le and bo[5] == le and bo[2] == le) or
    (bo[9] == le and bo[6] == le and bo[3] == le) or
    (bo[7] == le and bo[5] == le and bo[3] == le) or
    (bo[9] == le and bo[5] == le and bo[1] == le)) 
    
def getBoardCopy(board):
    # Создаёт копию игрового поля и возвращает его.
    boardCopy = []
    for i in board:
        boardCopy.append(i)
    return BoardCopy

def isSpaceFree(board, move):
    # Возвращает True, если сделан ход в свободную клетку.
    return board[move] == ' '

def getPlayerMove(board):
    # Разрешение  игроку сделать ход.
    move = ' '
    while move not in '1 2 3 4 5 6 7 8 9'.split() or not isSpaceFree(board, int(move)):
        print('Ваш следующий ход? (1-9)')
        move = input()
    return int(move)
    
def chooseRandomMoveFromList(board, movesList):
    # Возвращает допустимый ход, учитывая список сделанных ходов и список заполненных клеток.
    # Возвращает значение None, если больше нет допустимых ходов.
    possibleMoves = []
    for i in movesList:
        if isSpaceFree(board, i):
            possibleMoves.append(i)
            
    if len(possibleMoves) != 0:
        return random.choice(possibleMoves)
    else:
        return None
    
def getComputerMove(board, computerLetter):
    # Учитывая заполнение игрового поля и букву компьютера, определяет допустимый код и возвращает его.
    if computerLetter == 'X':
        playerLetter == 'O'
    else:
        playerLetter == 'X'
        
    # Это алгоритм для ИИ "Крестиков-Ноликов"
    # Сначала проверяем - победим ли мы, сделав следующий код.
    for i in range(1, 10):
        boardCopy = getBoardCopy(board)
        if isSpaceFree(boardCopy, i):
            makeMove(boardCopy, computerLetter, i)
            if isWinner (boardCopy, computerLetter):
                return i
            
    # Проверяем - победит ли игрок, сделав следующий ход, и блокируем его.
    for i in range(1,10):
        boardCopy = getBoardCopy(board)
        if isSpaceFree(boardCopy, i):
            makeMove(boardCopy, playerLetter, i)
            if isWinner (boardCopy, playerLetter):
                return i
            
    # Пробуем занять центр один из углов, если есть свободные.
    move = chooseRandomMoveFromList (board, [1, 3, 7, 9])
    if move != None:
        return move
    
    # Пробуем занять центр, если он свободен.
    if isSpaceFree(board, 5):
        return 5
    
    # Делаем ход по одной стороне.
    return chooseRandomMoveFromList (board, [2, 4, 6, 8])

def isBoardFull(board):
    # Возвращает True, если клетка на игровом поле занята. В противном случае, возвращает False.
    for i in range(1,10):
        if isSpaceFree(board, i):
            return False
    return True
    
    
print('Игра "Крестики-нолики"')

while True:
    # Презагрузка игрового поля
    theBoard = [' '] * 10
    playerLetter, computerLetter = inputPlayerLetter()
    turn = whoGoesFirst()
    print('' + turn + ' ходит первым.')
    gameIsPlaying = True
    
    while gameIsPlaying:
        if turn == 'Человек':
            # Ход игрока.
            drawBoard(theBoard)
            move = getPlayerMove(theBoard)
            makeMove(theBoard, playerLetter, move)
        
            if isWinner(theBoard, playerLetter):
                drawBoard(theBoard)
                print('Ура! Вы выиграли!')
                gameIsPlaying = False
        else:
            if isBoardFull(theBoard):
                drawBoard(theBoard)
                print('Ничья!')
                break
            else:
                turn = 'Компьютер'
                
    else:
        # Ход компьютера.
        move = getComputerMove(theBoard, computerLetter)
        makeMove(theBoard, computerLetter, move)
        
        if isWinner(theBoard, computerLetter):
            drawBoard(theBoard)
            print('Компьютер победил! Вы проиграли.')
            gameIsPlaying = False
        else:
            if isBoardFull(theBoard):
                drawBoard(theBoard)
                print('Ничья')
                break
            else:
                turn = 'Человек'
            
    print('Сыграем ещё раз раз? (да или нет)')
    if not input().lower().startswith('д'):
        break

Python крестики-нолики с компом (random)

Ребят, я новичок, помогите пж как сделать чтобы вместо 2 игрока ходил компьютер? Не нужно просчитывать каждый ход, прост рандомное место чтоб выбирал

    from random import randint
    
    while True:
        BOARD_SIZE = 3
        board = [i for i in range(9)]
        is_winner = False
        current_player = randint(0, 1)
        markers = {0: 'O', 1: 'X'}
        available_turns = (x for x in range(9))
        rchoice = random.choice(board)
    
        def draw_board():
            res = ""
            for i, v in enumerate(board):
                res += str(v) + " "
                if (i+1) % BOARD_SIZE == 0:
                     res += "\n"
            print(res)
    
        def validate(value = " "):
            if not value.isdigit() and int(value) not in available_turns:
                raise ValueError ("Enter valid value and try again")
            if board[int(value)] in ('X', 'O'):
                raise ValueError ("This value has already played")
            if '.' in value:
                raise ValueError('Number must be int')
    
        def computer():
            if current_player == 1:
                flag = True
                while flag:
                    rchoice = randint(0,8)
                    validate(int(rchoice))
                    flag = False
    
    
        def check_winner():
            current_marker = markers[current_player]
            if board[0] == current_marker and board[4] == current_marker and board[8] == current_marker or \
                    board[2] == current_marker and board[4] == current_marker and board[6] == current_marker or \
                    board[0] == current_marker and board[1] == current_marker and board[2] == current_marker or \
                    board[3] == current_marker and board[4] == current_marker and board[5] == current_marker or \
                    board[6] == current_marker and board[7] == current_marker and board[8] == current_marker or \
                    board[0] == current_marker and board[3] == current_marker and board[6] == current_marker or \
                    board[1] == current_marker and board[4] == current_marker and board[7] == current_marker or \
                    board[2] == current_marker and board[5] == current_marker and board[8] == current_marker:
                return True
            else:
                return False
    
        for i in range(9):
            try:
                input_error = True
                draw_board()
                while input_error:
                    computer()
                    choice = input(f"Player {markers[current_player]} enter your number:\n")
                    validate(choice)
                    input_error = False
                board[int(choice)] = markers[current_player]
                #проверить победителя
                is_winner = check_winner()
                if is_winner == True:
                    print(f'Player {markers[current_player]} won the game!!')
                    break
                current_player = 0 if current_player == 1 else 1
                # if is_winner == True:
            except ValueError as ex:
                print(ex)
    
        if is_winner == False:
            print('Draw')
        replay = input("Желаете переиграть? (Y or N)")
        if replay == "Y":
            continue
        else:
            break

И вот примерная функция компа, над её правильно сделать:

        def computer():
            if current_player == 1:
                flag = True
                while flag:
                    rchoice = randint(0,8)
                    validate(int(rchoice))
                    flag = False

Классическая игра в крестики-нолики в Python 3 | Джеймс Шах | Byte Tales

Что мы будем делать?

Мы собираемся создать игру в крестики-нолики для двух игроков, в которую мы сможем играть из командной строки. Сначала мы сделаем пустое игровое поле, а затем примем данные от игроков и проверим условие выигрыша, и если все поле будет заполнено и никто не выиграет, мы объявим результат как «Ничья». »И спросите пользователей, хотят ли они перезапустить игру.

Что мы будем использовать?

Мы создадим эту игру с использованием Python 3, поэтому убедитесь, что она установлена ​​на вашем ноутбуке / компьютере, и все готово.

Что мы узнаем?

После сборки этой игры мы можем получить довольно четкое представление о словарях в python, о том, как получить доступ к словарям, как перебирать словари, цикл for, условия if-else и функции в python.

Как работает игра?

Доска пронумерована как цифровая клавиатура. Таким образом, игрок может сделать свой ход на игровом поле, введя число с цифровой клавиатуры.

Плата пронумерована как цифровая клавиатура.

Code Time💻

Во-первых, давайте посмотрим, как мы собираемся использовать словарь для создания нашей игровой доски. Словарь — это примитивный тип данных в Python, который хранит данные в формате «ключ: значение». Таким образом, мы создадим словарь длиной 9, и каждая клавиша будет представлять блок на доске, а соответствующее ей значение будет представлять ход, сделанный игроком. и мы создадим функцию printBoard () , которую мы сможем использовать каждый раз, когда захотим распечатать обновленную доску в игре.

 Изначально наше игровое поле будет выглядеть так: | | 
- + - + -
| |
- + - + -
| |

Теперь в функции main мы сначала возьмем входные данные от игрока и проверим, является ли введенный ход правильным или нет. Если блок, в который игрок просит перейти, действителен, мы заполним этот блок, иначе мы попросим пользователя выбрать другой блок.

Теперь, чтобы проверить условие выигрыша, мы проверим в общей сложности 8 условий и, какой бы игрок ни сделал последний ход, мы объявим этого игрока победителем.А если никто не выиграет, мы объявляем «ничью»

. А теперь мы спросим игроков, хотят ли они играть снова.

И бум !! Теперь наша игра готова.

Полный код:

Время воспроизведения:

 | | 
- + - + -
| |
- + - + -
| |
Твоя очередь, X. Перейти в какое место?
7
X | |
- + - + -
| |
- + - + -
| |
Ваша очередь, О. Переезжайте в какое место?
9
X | | O
- + - + -
| |
- + - + -
| |
Ваша очередь, X.Куда переехать?
5
X | | O
- + - + -
| X |
- + - + -
| |
Ваша очередь, О. Переезжайте в какое место?
3
X | | O
- + - + -
| X |
- + - + -
| | O
Твоя очередь, X. Куда идти?
6
X | | O
- + - + -
| X | X
- + - + -
| | O
Ваша очередь, О. Переезжайте в какое место?
1
X | | O
- + - + -
| X | X
- + - + -
O | | O
Твоя очередь, X. Куда идти?
4
X | | O
- + - + -
X | X | X
- + - + -
O | | Игра окончена.**** X выиграл. ****

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

Крестики-нолики с использованием Python — AskPython

В этой статье мы рассмотрим этапы создания крестиков-ноликов с использованием языка Python из царапать.

Крестики-нолики Игровой процесс


Об игре

Крестики-нолики — это игра для двух игроков, в которую играют на квадратной сетке 3 × 3. Каждый игрок по очереди занимает ячейку с целью размещения трех отметок по горизонтали, вертикали или диагонали.Один игрок использует крест 'X' в качестве маркера, а другой использует нулевой 'O' .


Шаг 1: Дизайн крестиков-ноликов

Мы будем играть в крестики-нолики в командной строке, поэтому первое, что нам нужно сделать, это создать дизайн для наших крестиков-ноликов.

Дизайн крестиков-ноликов

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

 # Функция печати крестиков-ноликов
def print_tic_tac_toe (значения):
печать ("\ п")
печать ("\ t | |")
print ("\ t {} | {} | {}". формат (значения [0], значения [1], значения [2]))
print ('\ t _____ | _____ | _____')

печать ("\ t | |")
print ("\ t {} | {} | {}". формат (значения [3], значения [4], значения [5]))
print ('\ t _____ | _____ | _____')

печать ("\ t | |")

print ("\ t {} | {} | {}". формат (значения [6], значения [7], значения [8]))
печать ("\ t | |")
печать ("\ п")
 

В приведенном выше коде функция создает нашу игру в крестики-нолики в соответствии со значениями, переданными в качестве аргумента.Здесь аргумент значений — это список, содержащий статус каждой ячейки в сетке.


Шаг 2. Сохранение информации с помощью структур данных

Ядром любой игры является лежащая в ее основе игровая механика. Поскольку эту игру довольно легко создать, задействованная механика также проста.

В любой момент времени нам нужны две важные информации:

  • Состояние сетки — У нас должна быть структура данных, в которой хранится состояние каждой ячейки, то есть занята она или свободна.
  • Ходы каждого игрока Мы должны каким-то образом знать прошлые и настоящие ходы каждого игрока, то есть позиции, занятые 'X' и 'O' .

Примечание: Обе информации можно было бы получить, используя статус сетки, но требовалось бы пересекать ее каждый раз, когда нам нужны позиции игрока. Это можно назвать компромиссом между временем и сложностью пространства. Это общий метод экономии времени.

 # Функция для одиночной игры Tic Tac Toe
def single_game (cur_player):

# Представляет крестики-нолики
values ​​= ['' для x в диапазоне (9)]

# Сохраняет позиции, занятые X и O
player_pos = {'X': [], 'O': []}
 

Статус сетки управляется списком символов, который может иметь три возможных значения:

  • '' — свободная ячейка
  • 'X' — ячейка, занятая игроком X
  • 'O' — Ячейка, занятая игроком O

Ходы каждого игрока хранятся как словарь списка целых чисел.Ключи: 'X' и 'O' для соответствующего плеера. Соответствующие им списки содержат номера, присвоенные ячейкам сетки, которые они занимают.

Примечание: Переменная cur_player хранит текущего игрока, делающего ход, как в 'X' или 'O' .


Шаг 3: Цикл игры

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

 # Game Loop для одиночной игры Tic Tac Toe
в то время как True:
print_tic_tac_toe (значения)
 

Шаг 4: Обработка ввода игрока

На каждой итерации игры игрок должен вводить свой ход.

 # Попробовать блок исключений для ввода MOVE
пытаться:
print ("Игрок", cur_player, "поворот. Какой ящик?:", end = "")
переместить = интервал (ввод ())
кроме ValueError:
print («Неверный ввод !!! Попробуйте еще раз»)
Продолжать

# Проверка работоспособности для MOVE inout
если ход <1 или ход> 9:
print («Неверный ввод !!! Попробуйте еще раз»)
Продолжать

# Проверить, не занят ли ящик
если значения [move-1]! = '':
print ("Место уже заполнено.Попробуй еще раз!!")
Продолжать
 

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

Нам нужно выполнить некоторые проверки работоспособности, например, введенное значение является действительной позицией, и если это действительная позиция, она уже занята?


Шаг 5: Обновите информацию

В соответствии с вводом игрока нам необходимо обновить информацию для бесперебойного функционирования игры.

 # Обновить информацию об игре

# Обновление статуса сетки
значения [move-1] = cur_player

# Обновление позиций игроков
player_pos [cur_player] .append (переместить)
 

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

После обновления списка значений и вызова функции print_tic_tac_toe () сетка выглядит так:

Крестики-нолики после 5 поворотов.
Последний ход: ‘X’ at 2


Шаг 6: Проверка выигрыша или ничья

После каждого хода мы должны проверять, выиграл ли какой-либо игрок игру или игра была сделана вничью. Это можно проверить:

Вызов функций:

 # Вызов функции для проверки выигрыша
если check_win (player_pos, cur_player):
print_tic_tac_toe (значения)
print ("Игрок", cur_player, "выиграл игру !!")
печать ("\ п")
вернуть cur_player

# Вызов функции для проверки розыгрыша игры
если check_draw (player_pos):
print_tic_tac_toe (значения)
print ("Игра нарисована")
печать ("\ п")
вернуть 'D'
 

Если какой-либо игрок выигрывает, функция single_game () возвращает текущего игрока, который сделал ход.В случае ничьей, возвращается 'D' .

Функции:

 # Функция для проверки, выиграл ли какой-либо игрок
def check_win (player_pos, cur_player):

# Все возможные выигрышные комбинации
soln = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9] » , [1, 5, 9], [3, 5, 7]]

# Цикл, чтобы проверить, удовлетворена ли какая-либо выигрышная комбинация
для x в soln:
если все (y в player_pos [cur_player] для y в x):

# Возвращаем True, если какая-либо выигрышная комбинация удовлетворяет
вернуть True
# Возвращаем False, если комбинация не удовлетворяет
вернуть ложь

# Функция проверки, нарисована ли игра
def check_draw (player_pos):
если len (player_pos ['X']) + len (player_pos ['O']) == 9:
вернуть True
вернуть ложь
 

check_win () — Функция содержит все выигрышные комбинации.Все, что он делает, это проверяет, удовлетворяется ли какая-либо из выигрышных комбинаций текущими позициями игрока. Если это так, он возвращает True . Если ни одна из комбинаций не удовлетворена, функция возвращает False .

check_draw () — Условие розыгрыша довольно простое, так как игра заканчивается, когда все «девять» позиций заняты.


Шаг 7: Смена текущего игрока

Поскольку каждый игрок движется только один раз за раз, поэтому после каждого успешного хода мы должны поменять местами текущего игрока.

 # Переключить ходы игрока
если cur_player == 'X':
cur_player = 'О'
еще:
cur_player = 'X'
 

Что касается одиночной игры, это все, что нам нужно сделать. Но в этой статье также представлена ​​система табло для отслеживания, если игроки хотят сыграть в несколько игр.


Шаг 8: Введите имена игроков

На любом табло обязательно должны отображаться имена каждого игрока.

, если __name__ == "__main__":

print ("Игрок 1")
player1 = input ("Введите имя:")
печать ("\ п")

print ("Игрок 2")
player2 = input ("Введите имя:")
печать ("\ п")
 

Шаг 9: Сохранение информации, связанной с игрой

Необходимо сохранить такую ​​информацию, как текущий игрок, выбор игроков (взять крестик или ноль), доступные варианты (крестик и ноль) и табло.

 # Сохраняет игрока, который выбирает X и O
cur_player = player1

# Хранит выбор игроков
player_choice = {'X': "", 'O': ""}

# Сохраняет параметры
options = ['X', 'O']

# Хранит табло
score_board = {player1: 0, player2: 0}
 

По умолчанию текущий игрок — это игрок, который первым ввел имя.


Шаг 10: Дизайн табло

Табло хранится в виде словаря, где ключи — это имена игроков, а значения — их выигрышные числа.

 # Функция печати табло
def print_scoreboard (score_board):
Распечатать("--------------------------------")
print ("ДОСКА СЧЕТОВ")
Распечатать("--------------------------------")

игроки = список (score_board.keys ())
print ("", игроки [0], "", Score_board [игроки [0]])
print ("", игроки [1], "", Score_board [игроки [1]])

print ("-------------------------------- \ n")
 

Для отображения табло нам нужны имена игроков. Ключи извлекаются с помощью .keys () , а затем преобразуется в список, чтобы его можно было проиндексировать при отображении результатов.


Шаг 11: Внешний игровой цикл

Нам нужен еще один игровой цикл, для управления множественными совпадениями крестиков-ноликов. В каждом матче текущий игрок выбирает свою отметку ( 'X' или 'O' ). Меню для выбора должно отображаться на каждой итерации игры:

 # Game Loop для серии Tic Tac Toe.
# Цикл длится до тех пор, пока игроки не выйдут
в то время как True:

# Меню выбора игрока
print ("Поверните, чтобы выбрать", cur_player)
print ("Введите 1 для X")
print ("Введите 2 вместо O")
print («Введите 3, чтобы выйти»)
 

Табло и меню выглядят следующим образом:

Табло и меню


Шаг 12: Обработка и назначение выбора игрока

На каждой итерации мы должны обрабатывать и сохранять текущий выбор игрока.

 # Попробуйте исключение для ввода CHOICE
пытаться:
выбор = интервал (ввод ())
кроме ValueError:
print («Неправильный ввод !!! Попробуйте еще раз \ n»)
Продолжать

# Условия выбора игрока
если выбор == 1:
player_choice ['X'] = cur_player
если cur_player == player1:
player_choice ['O'] = player2
еще:
player_choice ['O'] = player1

elif choice == 2:
player_choice ['O'] = cur_player
если cur_player == player1:
player_choice ['X'] = player2
еще:
player_choice ['X'] = player1

elif choice == 3:
print («Окончательные результаты»)
print_scoreboard (табло_счетов)
сломать

еще:
print ("Неправильный выбор !!!! Попробуйте еще раз \ n")
 

По выбору игрока данные сохранены.Это важно, так как после завершения каждой игры мы узнаем, какой игрок выиграл.


Шаг 13: Проведите сопоставление

После сохранения всей необходимой информации пора выполнить независимое сопоставление и сохранить выигрышную отметку.

 # Сохраняет победителя в одиночной игре в крестики-нолики
победитель = single_game (варианты [выбор-1])
 

Шаг 14: Обновите табло

Нам нужно обновлять табло после каждого матча в крестики-нолики.

 # Обновляет табло в соответствии с победителем
если победитель! = 'D':
player_won = player_choice [победитель]
Score_board [player_won] = Score_board [player_won] + 1

print_scoreboard (табло_счетов)
 

Если игра не закончилась вничью, то обновляем табло.


Шаг 15: Сменить игрока, выбирающего

Это хорошая идея, что каждый игрок должен иметь возможность выбрать, какую отметку он хочет. Для этого мы меняем местами значение в cur_player .

 # Сменить игрока, который выбирает X или O
если cur_player == player1:
cur_player = player2
еще:
cur_player = player1
 

Полный рабочий код

 # Функция для печати крестиков-ноликов
def print_tic_tac_toe (значения):
печать ("\ п")
печать ("\ t | |")
print ("\ t {} | {} | {}".формат (значения [0], значения [1], значения [2]))
print ('\ t _____ | _____ | _____')

печать ("\ t | |")
print ("\ t {} | {} | {}". формат (значения [3], значения [4], значения [5]))
print ('\ t _____ | _____ | _____')

печать ("\ t | |")

print ("\ t {} | {} | {}". формат (значения [6], значения [7], значения [8]))
печать ("\ t | |")
печать ("\ п")


# Функция для печати табло
def print_scoreboard (score_board):
print ("\ t --------------------------------")
print ("\ t ДОСКА СЧЕТОВ")
print ("\ t --------------------------------")

player = list (score_board.ключи ())
print ("\ t", игроки [0], "\ t", табло [игроки [0]])
print ("\ t", игроки [1], "\ t", табло [игроки [1]])

print ("\ t -------------------------------- \ n")

# Функция, чтобы проверить, выиграл ли какой-либо игрок
def check_win (player_pos, cur_player):

# Все возможные выигрышные комбинации
soln = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7], [2, 5, 8], [3, 6, 9] » , [1, 5, 9], [3, 5, 7]]

# Цикл, чтобы проверить, удовлетворена ли какая-либо выигрышная комбинация
для x в soln:
если все (y в player_pos [cur_player] для y в x):

# Возвращаем True, если какая-либо выигрышная комбинация удовлетворяет
вернуть True
# Возвращаем False, если комбинация не удовлетворяет
вернуть ложь

# Функция проверки, нарисована ли игра
def check_draw (player_pos):
если len (player_pos ['X']) + len (player_pos ['O']) == 9:
вернуть True
вернуть ложь

# Функция для одиночной игры в крестики-нолики
def single_game (cur_player):

# Представляет крестики-нолики
values ​​= ['' для x в диапазоне (9)]

# Сохраняет позиции, занятые X и O
player_pos = {'X': [], 'O': []}

# Игровой цикл для одиночной игры в крестики-нолики
в то время как True:
print_tic_tac_toe (значения)

# Попробовать блок исключений для ввода MOVE
пытаться:
print ("Игрок", cur_player, "поворот.Какая коробка? : ", end =" ")
переместить = интервал (ввод ())
кроме ValueError:
print («Неверный ввод !!! Попробуйте еще раз»)
Продолжать

# Проверка работоспособности для MOVE inout
если ход <1 или ход> 9:
print («Неверный ввод !!! Попробуйте еще раз»)
Продолжать

# Проверить, не занят ли ящик
если значения [move-1]! = '':
print («Место уже заполнено. Попробуйте еще раз !!»)
Продолжать

# Обновить информацию об игре

# Обновление статуса сетки
значения [move-1] = cur_player

# Обновление позиций игроков
player_pos [cur_player].добавить (переместить)

# Вызов функции для проверки выигрыша
если check_win (player_pos, cur_player):
print_tic_tac_toe (значения)
print ("Игрок", cur_player, "выиграл игру !!")
печать ("\ п")
вернуть cur_player

# Вызов функции для проверки розыгрыша игры
если check_draw (player_pos):
print_tic_tac_toe (значения)
print ("Игра нарисована")
печать ("\ п")
вернуть 'D'

# Сменить ходы игрока
если cur_player == 'X':
cur_player = 'О'
еще:
cur_player = 'X'

если __name__ == "__main__":

print ("Игрок 1")
player1 = input ("Введите имя:")
печать ("\ п")

print ("Игрок 2")
player2 = input ("Введите имя:")
печать ("\ п")

# Сохраняет игрока, который выбирает X и O
cur_player = player1

# Хранит выбор игроков
player_choice = {'X': "", 'O': ""}

# Сохраняет параметры
options = ['X', 'O']

# Хранит табло
score_board = {player1: 0, player2: 0}
print_scoreboard (табло_счетов)

# Game Loop для серии Tic Tac Toe
# Цикл длится до тех пор, пока игроки не выйдут
в то время как True:

# Меню выбора игрока
print ("Поверните, чтобы выбрать", cur_player)
print ("Введите 1 для X")
print ("Введите 2 вместо O")
print («Введите 3, чтобы выйти»)

# Попробуйте исключение для ввода CHOICE
пытаться:
выбор = интервал (ввод ())
кроме ValueError:
print («Неправильный ввод !!! Попробуйте еще раз \ n»)
Продолжать

# Условия выбора игрока
если выбор == 1:
player_choice ['X'] = cur_player
если cur_player == player1:
player_choice ['O'] = player2
еще:
player_choice ['O'] = player1

elif choice == 2:
player_choice ['O'] = cur_player
если cur_player == player1:
player_choice ['X'] = player2
еще:
player_choice ['X'] = player1

elif choice == 3:
print («Окончательные результаты»)
print_scoreboard (табло_счетов)
сломать

еще:
print ("Неправильный выбор !!!! Попробуйте еще раз \ n")

# Сохраняет победителя в одиночной игре в крестики-нолики
победитель = single_game (варианты [выбор-1])

# Редактирует табло в соответствии с победителем
если победитель! = 'D':
player_won = player_choice [победитель]
Score_board [player_won] = Score_board [player_won] + 1

print_scoreboard (табло_счетов)
# Сменить игрока, который выбирает X или O
если cur_player == player1:
cur_player = player2
еще:
cur_player = player1
 

Пора поиграть!

Все шаги по созданию игры завершены.Пришло время поиграть в игру.

 Игрок 1
Введите имя: Луффи


Игрок 2
Введите имя: Санджи


--------------------------------
ТАБЛО
--------------------------------
Луффи 0
Санджи 0
--------------------------------

Повернись, чтобы выбрать Луффи
Введите 1 для X
Введите 2 для O
Введите 3, чтобы выйти
1


| |
| |
_____ | _____ | _____
| |
| |
_____ | _____ | _____
| |
| |
| |


Игрок X ход.Какая коробка? : 5


| |
| |
_____ | _____ | _____
| |
| X |
_____ | _____ | _____
| |
| |
| |


Игрок O ход. Какая коробка? : 1


| |
O | |
_____ | _____ | _____
| |
| X |
_____ | _____ | _____
| |
| |
| |


Игрок X ход. Какая коробка? : 9


| |
O | |
_____ | _____ | _____
| |
| X |
_____ | _____ | _____
| |
| | Икс
| |


Игрок O ход.Какая коробка? : 2


| |
O | O |
_____ | _____ | _____
| |
| X |
_____ | _____ | _____
| |
| | Икс
| |


Игрок X ход. Какая коробка? : 3


| |
O | O | Икс
_____ | _____ | _____
| |
| X |
_____ | _____ | _____
| |
| | Икс
| |


Игрок O ход. Какая коробка? : 7


| |
O | O | Икс
_____ | _____ | _____
| |
| X |
_____ | _____ | _____
| |
O | | Икс
| |


Игрок X ход.Какая коробка? : 6


| |
O | O | Икс
_____ | _____ | _____
| |
| X | Икс
_____ | _____ | _____
| |
O | | Икс
| |


Игрок X выиграл игру !!


--------------------------------
ТАБЛО
--------------------------------
Луффи 1
Санджи 0
--------------------------------

Повернись, чтобы выбрать Санджи
Введите 1 для X
Введите 2 для O
Введите 3, чтобы выйти
2


| |
| |
_____ | _____ | _____
| |
| |
_____ | _____ | _____
| |
| |
| |


Игрок O ход.Какая коробка? : 5


| |
| |
_____ | _____ | _____
| |
| O |
_____ | _____ | _____
| |
| |
| |


Игрок X ход. Какая коробка? : 3


| |
| | Икс
_____ | _____ | _____
| |
| O |
_____ | _____ | _____
| |
| |
| |


Игрок O ход. Какая коробка? : 2


| |
| O | Икс
_____ | _____ | _____
| |
| O |
_____ | _____ | _____
| |
| |
| |


Игрок X ход.Какая коробка? : 8


| |
| O | Икс
_____ | _____ | _____
| |
| O |
_____ | _____ | _____
| |
| X |
| |


Игрок O ход. Какая коробка? : 1


| |
O | O | Икс
_____ | _____ | _____
| |
| O |
_____ | _____ | _____
| |
| X |
| |


Игрок X ход. Какая коробка? : 9


| |
O | O | Икс
_____ | _____ | _____
| |
| O |
_____ | _____ | _____
| |
| X | Икс
| |


Игрок O ход.Какая коробка? : 6


| |
O | O | Икс
_____ | _____ | _____
| |
| O | О
_____ | _____ | _____
| |
| X | Икс
| |


Игрок X ход. Какая коробка? : 7


| |
O | O | Икс
_____ | _____ | _____
| |
| O | О
_____ | _____ | _____
| |
X | X | Икс
| |


Игрок X выиграл игру !!


--------------------------------
ТАБЛО
--------------------------------
Луффи 2
Санджи 0
--------------------------------

Повернись, чтобы выбрать Луффи
Введите 1 для X
Введите 2 для O
Введите 3, чтобы выйти
3
Итоговые результаты
--------------------------------
ТАБЛО
--------------------------------
Луффи 2
Санджи 0
--------------------------------
 


Заключение

Мы надеемся, что эта статья была интересной и информативной для читателя.Я также загрузил код на Github. Вы можете посетить здесь, чтобы получить код. Если есть какие-то предложения по игре, не стесняйтесь комментировать.


Python Tic Tac Toe — разработка игры на Python

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

С помощью этого проекта Python от TechVidvan мы собираемся создать интерактивную игру «Крестики-нолики», в которой мы будем узнавать что-то новое по пути.

Что такое крестики-нолики?

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

В игру играют два человека. Сначала мы рисуем доску с квадратной сеткой 3 × 3. Первый игрок выбирает «X» и рисует его на любой квадратной сетке, тогда у второго игрока появляется шанс нарисовать «O» на доступных клетках.Таким образом, игроки поочередно рисуют «X» и «O» на пустых местах до тех пор, пока игроку не удастся нарисовать 3 последовательных знака по горизонтали, вертикали или диагонали. Затем игрок выигрывает игру, в противном случае игра заканчивается, когда все места заполнены.

Python Tic Tac Toe — подробности проекта

Интересный проект Python будет построен с использованием библиотеки pygame. Мы объясним все методы объекта pygame, которые используются в этом проекте. Pygame — отличная библиотека, которая позволит нам создавать окна и рисовать в них изображения и формы.Таким образом мы захватим координаты мыши и определим блок, где нам нужно отметить «X» или «O». Затем мы проверим, выиграл пользователь игру или нет.

Скачать код проекта Tic Tac Toe

Пожалуйста, загрузите полный исходный код проекта Tic Tac Toe: Tic Tac Toe Python Project

Предварительные требования

Для реализации этой игры мы будем использовать базовые концепции Python и Pygame, которые — это библиотека Python для создания кроссплатформенных игр. Он содержит модули, необходимые для компьютерной графики и звуковых библиотек.Чтобы установить библиотеку, вы можете использовать установщик pip из командной строки:

 pip install pygame 

Шаги по созданию игры Python Tic Tac Toe

Сначала давайте проверим шаги по созданию программы Tic Tac Toe на Python:

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

Нам нужно запустить нашу игру в бесконечном цикле. Он будет постоянно искать события, и когда пользователь нажимает кнопку мыши на сетке, мы сначала получаем координаты X и Y мыши. Затем мы проверим, какой квадрат щелкнул пользователь. Затем мы нарисуем на холсте соответствующее изображение «X» или «O». Так что в основном это то, что мы будем делать в этой идее проекта Python.

1. Инициализация игровых компонентов

Итак, давайте начнем с импорта библиотеки pygame и библиотеки времени, потому что мы будем использовать время .sleep () для приостановки игры в определенных положениях. Затем мы инициализируем все глобальные переменные, которые мы будем использовать в нашей игре «Крестики-нолики».

 импортировать pygame как pg, sys
из импорта pygame.locals *
время импорта


#initialize глобальные переменные
XO = 'х'
победитель = Нет
draw = Ложь
ширина = 400
высота = 400
белый = (255, 255, 255)
line_color = (10,10,10)

Доска #TicTacToe 3x3
TTT = [[None] * 3, [None] * 3, [None] * 3] 

Здесь TTT — это основная доска 3×3 Tic Tac Toe, и сначала она будет иметь 9 значений None.Высота и ширина холста, на котором мы будем играть, — 400 × 400.

2. Инициализация окна Pygame

Мы используем pygame для создания нового окна, в котором мы будем играть в нашу игру «Крестики-нолики». Итак, мы инициализируем pygame с помощью метода pg.init () , и для отображения окна устанавливается ширина 400 и высота 500. Мы зарезервировали 100-пиксельное пространство для отображения статуса игры.

pg.display.set_mode () инициализирует отображение, и мы ссылаемся на него с помощью экранной переменной.Эта экранная переменная будет использоваться всякий раз, когда мы хотим что-то нарисовать на дисплее.

Метод pg.display.set_caption используется для установки имени, которое будет отображаться в верхней части окна дисплея.

 # инициализация окна pygame
pg.init ()
кадров в секунду = 30
ЧАСЫ = pg.time.Clock ()
screen = pg.display.set_mode ((ширина, высота + 100), 0,32)
pg.display.set_caption («Крестики-нолики»)
 

3. Загрузка и преобразование изображений

В проекте Python используется множество изображений, например начальное изображение, которое будет отображаться при запуске или сбросе игры.Изображения X и O, которые мы будем рисовать, когда пользователь нажимает на сетку. Мы загружаем все изображения и изменяем их размер, чтобы они легко помещались в нашем окне.

 # загрузка изображений
открытие = pg.image.load ('tic tac Opening.png')
x_img = pg.image.load ('x.png')
o_img = pg.image.load ('o.png')

# изменение размера изображений
x_img = pg.transform.scale (x_img, (80,80))
o_img = pg.transform.scale (o_img, (80,80))
open = pg.transform.scale (open, (width, height + 100)) 

4. Определите функции

Теперь мы создадим функцию, которая запустит игру.Мы также будем использовать эту функцию, когда захотим перезапустить игру. В pygame функция blit () используется на поверхности для рисования изображения поверх другого изображения.

Итак, мы рисуем начальное изображение, и после рисования нам всегда нужно обновлять отображение с помощью pg.display.update () . Когда начальное изображение нарисовано, мы ждем 1 секунду, используя time.sleep (1) , и заполняем экран белым цветом.

Затем мы рисуем 2 вертикальные и горизонтальные линии на белом фоне, чтобы получилась сетка 3 × 3.В конце вызываем функцию draw_status ()

 def game_opening ():
    screen.blit (открытие, (0,0))
    pg.display.update ()
    время сна (1)
    screen.fill (белый)

    # Рисование вертикальных линий
    pg.draw.line (экран, цвет_линии, (ширина / 3,0), (ширина / 3, высота), 7)
    pg.draw.line (экран, цвет_линии, (ширина / 3 * 2,0), (ширина / 3 * 2, высота), 7)
    # Рисование горизонтальных линий
    pg.draw.line (экран, цвет_линии, (0, высота / 3), (ширина, высота / 3), 7)
    pg.draw.line (экран, цвет_линии, (0, высота / 3 * 2), (ширина, высота / 3 * 2), 7)
    draw_status ()
 

Функция draw_status () рисует черный прямоугольник, в котором мы обновляем статус игры, показывая, у какого игрока сейчас ход, и закончилась ли игра или ничья.

 def draw_status ():
    глобальный розыгрыш

    если победитель - Нет:
        message = XO.upper () + "Поворот"
    еще:
        message = Winner.upper () + "выиграл!"
    если ничья:
        message = 'Game Draw!'

    font = pg.font.Font (Нет, 30)
    text = font.render (сообщение, 1, (255, 255, 255))

    # копируем обработанное сообщение на доску
    screen.fill ((0, 0, 0), (0, 400, 500, 100))
    text_rect = text.get_rect (center = (ширина / 2, 500-50))
    screen.blit (текст, text_rect)
    pg.display.update () 

Функция check_win () проверяет доску Tic Tac Toe, чтобы увидеть все отметки «X» и «O».Он рассчитывает, выиграл ли игрок игру или нет. Они могут выиграть, если игрок поставил 3 последовательных метки в строке, столбце или по диагонали. Эта функция вызывается каждый раз, когда мы рисуем на доске отметку «X» или «O».

 def check_win ():
    глобальный ТТТ, победитель, ничья

    # проверить выигрышные строки
    для строки в диапазоне (0,3):
        если ((TTT [строка] [0] == TTT [строка] [1] == TTT [строка] [2]) и (TTT [строка] [0] не равно None)):
            # эта строка выиграла
            победитель = TTT [строка] [0]
            стр.draw.line (screen, (250,0,0), (0, (row + 1) * height / 3 -height / 6), \
                              (ширина, (строка + 1) * высота / 3 - высота / 6), 4)
            сломать

    # проверьте выигрышные столбцы
    для столбца в диапазоне (0, 3):
        if (TTT [0] [col] == TTT [1] [col] == TTT [2] [col]) и (TTT [0] [col] не равно None):
            # этот столбец выиграл
            Победитель = TTT [0] [столбец]
            #draw выигрышная линия
            pg.draw.line (экран, (250,0,0), ((col + 1) * width / 3 - width / 6, 0), \
                          ((col + 1) * width / 3 - ширина / 6, высота), 4)
            сломать

    # проверить диагональных победителей
    если (TTT [0] [0] == TTT [1] [1] == TTT [2] [2]) и (TTT [0] [0] не равно None):
        # выигранная игра по диагонали слева направо
        победитель = TTT [0] [0]
        стр.draw.line (экран, (250,70,70), (50, 50), (350, 350), 4)

    если (TTT [0] [2] == TTT [1] [1] == TTT [2] [0]) и (TTT [0] [2] не равно None):
        # игра выиграна по диагонали справа налево
        Победитель = TTT [0] [2]
        pg.draw.line (экран, (250,70,70), (350, 50), (50, 350), 4)

    если (все ([все (строка) для строки в TTT]) и победитель - Нет):
        draw = True
    draw_status () 

Функция drawXO (row, col) берет строку и столбец, в которых выполняется щелчок мышью, а затем рисует метку «X» или «O».Мы вычисляем координаты x и y начальной точки, откуда мы будем рисовать png-изображение метки.

 def drawXO (row, col):
    глобальный TTT, XO
    если row == 1:
        posx = 30
    если строка == 2:
        posx = ширина / 3 + 30
    если строка == 3:
        posx = ширина / 3 * 2 + 30

    если col == 1:
        posy = 30
    если col == 2:
        posy = высота / 3 + 30
    если col == 3:
        posy = высота / 3 * 2 + 30
    TTT [row-1] [col-1] = XO
    если (XO == 'x'):
        screen.blit (x_img, (posy, posx))
        XO = 'о'
    еще:
        экран.блит (o_img, (posy, posx))
        XO = 'х'
    pg.display.update ()
    #print (posx, posy)
    #print (TTT)
 

Функция userClick () запускается каждый раз, когда пользователь нажимает кнопку мыши.

Когда пользователь щелкает мышью, мы сначала берем координаты x и y точки щелчка мыши в окне дисплея, а затем, если это место не занято, мы рисуем «XO» на холсте. Мы также проверяем, выиграл ли игрок или нет, после того, как нарисовал на доске «XO».

 def userClick ():
    # получить координаты щелчка мышью
    х, у = стр.mouse.get_pos ()

    #get столбец щелчка мыши (1-3)
    если (x <ширина / 3):
        col = 1
    elif (x <ширина / 3 * 2):
        col = 2
    elif (x <ширина):
        col = 3
    еще:
        col = Нет

    # получить строку щелчка мыши (1-3)
    если (y <высота / 3):
        row = 1
    elif (y <высота / 3 * 2):
        row = 2
    elif (y <высота):
        row = 3
    еще:
        row = None
    #print (строка, столбец)

    если (row и col и TTT [row-1] [col-1] - None):
        глобальный XO

        # нарисуйте x или o на экране
        drawXO (строка; столбец)
        check_win ()
 

Последняя функция - это reset_game ().Это перезапустит игру, и мы также сбросим все переменные на начало игры.

 def reset_game ():
    глобальный ТТТ, победитель, XO, ничья
    время сна (3)
    XO = 'х'
    draw = Ложь
    game_opening ()
    победитель = Нет
    TTT = [[None] * 3, [None] * 3, [None] * 3] 

5. Запустить игру в крестики-нолики навсегда

Чтобы начать игру, мы вызовем функцию game_opening () . Затем мы запускаем бесконечный цикл и постоянно проверяем любое событие, сделанное пользователем. Если пользователь нажимает кнопку мыши, событие MOUSEBUTTONDOWN будет захвачено, а затем мы вызовем функцию userClick () .Затем, если пользователь выигрывает или игра заканчивается ничьей, мы сбрасываем игру, вызывая функцию reset_game () . Мы обновляем отображение на каждой итерации, и мы установили 30 кадров в секунду.

 game_opening ()

# запускаем игровой цикл навсегда
в то время как (Истина):
    для события в pg.event.get ():
        если event.type == ВЫЙТИ:
            pg.quit ()
            sys.exit ()
        elif event.type == MOUSEBUTTONDOWN:
            # пользователь щелкнул; поместите X или O
            userClick ()
            если (победитель или ничья):
                reset_game ()

    стр.display.update ()
    CLOCK.tick (fps) 

Ура! Игра завершена и готова к игре. Сохраните исходный код с именем файла tictactoe.py и запустите файл.

Вывод:

Резюме

С помощью этого проекта на Python мы успешно создали игру «Крестики-нолики». Мы использовали популярную библиотеку pygame для рендеринга графики в окне дисплея. Мы узнали, как захватывать события с клавиатуры или мыши и запускать функцию при нажатии кнопки мыши.Таким образом, мы можем вычислить положение мыши, нарисовать X или O на дисплее и проверить, выиграл ли игрок игру или нет. Надеюсь, вам понравилось создавать игру.

А теперь пора поработать над еще одним интересным проектом - Python Project on Typing Speed ​​Test

Возникли трудности при работе над проектом Python с исходным кодом? Делитесь в комментариях. Наши специалисты TechVidvan всегда готовы помочь вам.

Python реализация автоматической игры Tic Tac Toe с использованием случайного числа

import numpy as np

import random

from time import sleep

def create_board ():

return (np.массив ([[ 0 , 0 , 0 ],

[ 0 , 0 , 0 ],

[ 0 , 0 , 0 ]])

def возможности (плата):

l = []

для i в диапазоне ( len (доска)):

для j дюйм диапазон ( длина (плата)):

9000 7

если доска [i] [j] = = 0 :

л.append ((i, j))

return (l)

def random_place (доска, игрок):

selection = возможности (доска)

current_loc = random.choice (selection)

board [current_loc] = player

return (board)

def row_win (доска, игрок):

для x в диапазоне ( len (доска)):

win = True

90 036 для y в диапазоне ( len (доска)):

if board [x, y]! = игрок:

выиграть = Ложь

продолжить

если выиграть = = Верно :

возврат (выигрыш)

возврат (выигрыш)

def col_win (доска, игрок):

для x в диапазоне ( len (доска)):

win = True

для y дюйм 9 0036 диапазон ( len (доска)):

if board [y] [x]! = игрок:

выиграть = Ложь

продолжить

если выиграть = = Верно :

возврат (выигрыш)

возврат (выигрыш)

def diag_win (доска, игрок):

win = True

y = 0

для x в диапазоне ( len (доска)):

если доска [x, x]! = игрок:

выигрыш = Ложь

если выигрыш:

возврат выигрыш

выигрыш выигрыш = True

if win:

для x в диапазоне ( len (доска)):

y = len (доска) - 1 - x

если доска [x, y]! = игрок:

выигрыш = Ложный

возврат выигрыш

def оценка (доска):

Победитель = 0

для игрок дюйм [ 1 , 2 ]:

если (row_win (доска, игрок) или

col_win (доска, игрок) или

diag_win (доска, игрок)):

победитель = игрок 9 0036

если нп. все (доска! = 0 ) и победитель = = 0 :

победитель = - 1

возврат победитель

def play_game ():

доска, победитель, счетчик = create_board (), 0 , 1

печать (доска)

спящий ( 2 )

в то время как победитель = = 0 :

для игрок в [ 1 , 2 ]:

доска = random_place (доска, игрок)

печать ( ) «Доска после» + str (счетчик) + «переместить» )

печать (доска)

спящий ( 2 )

счетчик + = 1

победитель = оценить (доска)

если победитель! = 0 :

перерыв

возврат (победитель)

печать ( "Победитель:" + str (play_game ()))

Игра в крестики-нолики на Python

В этой статье я покажу вам, как создать очень простую игру в крестики-нолики в консольном приложении C #.

Введение

Крестики-нолики - очень простая игра для двух игроков. Таким образом, одновременно могут играть только два игрока. Эта игра также известна как крестики-нолики или крестики-нолики. Один игрок играет с X, а другой - с O. В этой игре у нас есть доска, состоящая из сетки 3x3. Количество сеток может быть увеличено.

Доска Tic-Tac-Toe выглядит следующим образом:

Правила игры

  1. Традиционно первый игрок играет с «Х».Таким образом, вы можете решить, кто хочет выбрать «X», а кто - «O».
  2. Одновременно может играть только один игрок.
  3. Если какой-либо из игроков заполнил квадрат, то другой игрок и тот же игрок не могут отменить этот квадрат.
  4. Есть только два условия, при которых может совпадать ничья или может быть победа.
  5. Игрок, которому удастся разместить три соответствующих знака (X или O) в горизонтальном, вертикальном или диагональном ряду, выигрывает игру.

Условие выигрыша

Победителем становится тот, кто поставит три соответствующие отметки (X или O) по горизонтали, вертикали или диагонали.

Код игры следующий:

  1. импорт ОС
  2. импортное время
  3. board = ['', '', '', '', '', '', '', '', '', '']
  4. игрока = 1
  5. ######## флаги победы ##########
  6. Win = 1
  7. Ничья = -1
  8. Работает = 0
  9. Стоп = 1
  10. ##########################
  11. Game = Запуск
  12. Отметка = 'X'
  13. # Эта функция рисует игровое поле
  14. def DrawBoard ():
  15. print ("% c |% c |% c"% (доска [1], доска [2], доска [3]))
  16. печать ("___ | ___ | ___")
  17. print ("% c |% c |% c"% (доска [4], доска [5], доска [6]))
  18. печать ("___ | ___ | ___")
  19. print ("% c |% c |% c"% (доска [7], доска [8], доска [9]))
  20. печать ("| |")
  21. # Эта функция проверяет пустую позицию или нет
  22. def CheckPosition (x):
  23. if (board [x] == ''):
  24. возврат True
  25. еще:
  26. возврат Ложь
  27. # Эта функция проверяет, выиграл ли игрок
  28. def CheckWin ():
  29. глобальная игра
  30. # Условие выигрыша по горизонтали
  31. if (доска [1] == доска [2] и доска [2] == доска [3] и доска [1]! = ''):
  32. Игра = Победа
  33. elif (доска [4] == доска [5] и доска [5] == доска [6] и доска [4]! = ''):
  34. Игра = Победа
  35. elif (доска [7] == доска [8] и доска [8] == доска [9] и доска [7]! = ''):
  36. Игра = Победа
  37. # Условие победы по вертикали
  38. elif (доска [1] == доска [4] и доска [4] == доска [7] и доска [1]! = ''):
  39. Игра = Победа
  40. elif (доска [2] == доска [5] и доска [5] == доска [8] и доска [2]! = ''):
  41. Игра = Победа
  42. elif (доска [3] == доска [6] и доска [6] == доска [9] и доска [3]! = ''):
  43. Игра = Победа
  44. # Диагональное условие выигрыша
  45. elif (доска [1] == доска [5] и доска [5] == доска [9] и доска [5]! = ''):
  46. Игра = Победа
  47. elif (доска [3] == доска [5] и доска [5] == доска [7] и доска [5]! = ''):
  48. Игра = Победа
  49. # Условие ничьей или ничьей
  50. elif (board [1]! = '' And board [2]! = '' And board [3]! = '' And board [4]! = '' And board [5]! = '' And board [ 6]! = '' И доска [7]! = '' И доска [8]! = '' И доска [9]! = ''):
  51. Игра = Ничья
  52. еще:
  53. Game = Запуск
  54. принт («Игра в крестики-нолики, созданная Сурабом Сомани»)
  55. print («Игрок 1 [X] --- Игрок 2 [O] \ n»)
  56. печать ()
  57. печать ()
  58. print ("Подождите... ")
  59. время сна (3)
  60. пока (Игра == Бег):
  61. os.system ('cls')
  62. Доска для рисования ()
  63. если (игрок% 2! = 0):
  64. отпечаток («Шанс первого игрока»)
  65. Отметка = 'X'
  66. еще:
  67. print («Шанс игрока 2»)
  68. Марка = 'O'
  69. choice = int (input («Введите позицию между [1–9], где вы хотите отметить:»))
  70. если (CheckPosition (выбор)):
  71. Доска

  72. [выбор] = Марка
  73. игрока + = 1
  74. CheckWin ()
  75. os.система ('cls')
  76. Доска для рисования ()
  77. если (Игра == Ничья):
  78. принт («Игра в розыгрыш»)
  79. Элиф (Игра == Победа):
  80. игрок- = 1
  81. , если (игрок% 2! = 0):
  82. print («Игрок 1 выиграл»)
  83. еще:
  84. print («Игрок 2 выиграл»)

Выход

Глава 10 - Крестики-Нолики

10
TIC-TAC-TOE

В этой главе рассказывается об игре «Крестики-нолики».В крестики-нолики обычно играют вдвоем. Один игрок имеет размер X , а другой - O . Игроки по очереди кладут свои X или O . Если игрок получает три своих знака на доске в ряд, столбец или диагональ, он выигрывает. Когда поле заполняется без выигрыша ни одного игрока, игра заканчивается вничью.

Эта глава не знакомит с большим количеством новых концепций программирования. Пользователь будет играть против простого искусственного интеллекта, который мы напишем, используя имеющиеся у нас знания в области программирования.Искусственный интеллект (AI) - это компьютерная программа, которая может разумно реагировать на движения игрока. ИИ, который играет в крестики-нолики, не сложен; на самом деле это всего лишь несколько строк кода.

Давайте начнем с просмотра примера выполнения программы. Игрок делает свой ход, вводя номер клетки, которую он хочет занять. Чтобы помочь нам запомнить, какой индекс в списке предназначен для какого места, мы пронумеруем доску, как цифровую клавиатуру, как показано на рисунке 10-1.

Рисунок 10-1: Плата пронумерована, как цифровая клавиатура.

Образец теста Tic-Tac-Toe

Вот что видит пользователь, когда запускает программу «Крестики-нолики». Текст, который вводит игрок, выделен жирным шрифтом.

Добро пожаловать в крестики-нолики!
Вы хотите быть X или O?
X
Компьютер пойдет первым.
O | |
- + - + -
| |
- + - + -
| |
Каков ваш следующий шаг? (1-9)
3
O | |
- + - + -
| |
- + - + -
O | | X

Какой ваш следующий шаг? (1-9)
4
O | | O
- + - + -
X | |
- + - + -
O | | X
Каков ваш следующий шаг? (1-9)
5
O | O | O
- + - + -
X | X |
- + - + -
O | | X
Компьютер тебя обыграл! Ты проиграл.
Хочешь сыграть снова? (да или нет)
нет

Исходный код для Tic-Tac-Toe

В новом файле введите следующий исходный код и сохраните его как tictactoe.py . Затем запустите игру, нажав F5. Если вы получаете ошибки, сравните набранный вами код с кодом книги с онлайн-инструментом сравнения по адресу https://www.nostarch.com/inventwithpython#diff .

tictactoe.py

1. # Крестики-нолики
2.
3.import random
4.
5. def drawBoard (board):
6. # Эта функция распечатывает доску, которую она передала.
7.
8. # "board" - это список из 10 строк, представляющих доску (игнорируйте
индекс 0).
9. печать (доска [7] + '|' + доска [8] + '|' + доска [9])
10. печать ('- + - + -')
11. печать (доска [4] + '|' + доска [5] + '|' + доска [6])
12. print ('- + - + -')
13. print (доска [1] + '|' + доска [2] + '|' + доска [3])
14.
15. def inputPlayerLetter ():
16. # Позволяет игроку вводить букву, которой он хочет быть.
17. # Возвращает список, в котором буква игрока является первым элементом, а буква компьютера
- вторым.
18. letter = ''
19. пока нет (буква == 'X' или буква == 'O'):
20. print ('Вы хотите быть X или O?')
21. letter = input (). upper ()
22.
23. # Первый элемент в списке - буква игрока; второй -
буква компьютера.
24. if letter == 'X':
25. return ['X', 'O']
26. else:
27. return ['O', 'X']
28.
29. def whoGoesFirst ():
30. # Случайно выбрать, какой игрок ходит первым.
31. if random.randint (0, 1) == 0:
32. return 'computer'
33. else:
34. return 'player'
35.
36. def makeMove (доска, буква, ход) :
37. board [move] = letter
38.
39. def isWinner (bo, le):
40.# Учитывая доску и букву игрока, эта функция возвращает True, если
этот игрок выиграл.
41. # Мы используем «bo» вместо «board» и «le» вместо «letter», поэтому нам не нужно набирать так много текста, как
.
42. return ((bo [7] == le и bo [8] == le и bo [9] == le) или # В верхней части

43. (bo [4] == le и bo [ 5] == le и bo [6] == le) или # через середину
44. (bo [1] == le и bo [2] == le и bo [3] == le) или # через нижняя
45.(bo [7] == le и bo [4] == le и bo [1] == le) или # Внизу слева
46. (bo [8] == le и bo [5] == le and bo [2] == le) или # вниз по центру
47. (bo [9] == le и bo [6] == le и bo [3] == le) или # вниз по правой стороне

48. (bo [7] == le и bo [5] == le и bo [3] == le) или # Diagonal
49. (bo [9] == le и bo [5] == le и bo [1] == le)) # Диагональ
50.
51. def getBoardCopy (board):
52. # Сделайте копию списка плат и верните ее.
53. boardCopy = []
54. for i in board:
55. boardCopy.append (i)
56. return boardCopy
57.
58. def isSpaceFree (board, move):
59. # Вернуть True, если пропущенный ход бесплатно на переданной доске.
60. return board [move] == ''
61.
62. def getPlayerMove (board):
63. # Позвольте игроку ввести свой ход.
64. move = ''
65. while move not in '1 2 3 4 5 6 7 8 9'. Split () или нет
isSpaceFree (board, int (move)):
66.print ('Каким будет ваш следующий ход? (1-9)')
67. move = input ()
68. return int (move)
69.
70. def chooseRandomMoveFromList (board, moveList):
71. # Возвращает действительный ход из переданного списка на переданной доске.
72. # Не возвращает None, если нет допустимого хода.
73. possibleMoves = []
74. для i в списке ходов:
75. если isSpaceFree (доска, i):
76. possibleMoves.append (i)
77.
78.if len (possibleMoves)! = 0:
79. return random.choice (possibleMoves)
80. else:
81. return None
82.
83. def getComputerMove (board, computerLetter):
84. # Дана плата и буква компьютера, определите, куда переместить
, и вернитесь к этому ходу.
85. if computerLetter == 'X':
86. playerLetter = 'O'
87. else:
88. playerLetter = 'X'
89.
90. # Вот алгоритм для наших крестиков-ноликов AI:
91.# Сначала проверьте, сможем ли мы выиграть следующим ходом.
92. for i in range (1, 10):
93. boardCopy = getBoardCopy (board)
94. if isSpaceFree (boardCopy, i):
95. makeMove (boardCopy, computerLetter, i)
96. if isWinner ( boardCopy, computerLetter):
97. return i
98.
99. # Проверьте, может ли игрок выиграть на следующем ходу, и заблокируйте его.
100. для i в диапазоне (1, 10):
101. boardCopy = getBoardCopy (доска)
102.if isSpaceFree (boardCopy, i):
103. makeMove (boardCopy, playerLetter, i)
104. if isWinner (boardCopy, playerLetter):
105. return i
106.
107. # Попробуйте взять один из углов, если они свободны.
108. move = chooseRandomMoveFromList (board, [1, 3, 7, 9])
109. if move! = None:
110. return move
111.
112. # Попробуйте занять центр, если он свободен .
113. Если isSpaceFree (доска, 5):
114.return 5
115.
116. # Двигайтесь по одной из сторон.
117. return chooseRandomMoveFromList (board, [2, 4, 6, 8])
118.
119. def isBoardFull (board):
120. # Вернуть True, если все места на доске были заняты. В противном случае
вернет False.
121. for i in range (1, 10):
122. if isSpaceFree (board, i):
123. return False
124. return True
125.
126.
127. print ('Добро пожаловать в Tic- Нолики! ')
128.
129. а True:
130. # Сбросить плату.
131. theBoard = [''] * 10
132. playerLetter, computerLetter = inputPlayerLetter ()
133. turn = whoGoesFirst ()
134. print ('+ turn +' пойдет первым. ')
135. gameIsPlaying = True
136.
137. while gameIsPlaying:
138. if turn == 'player':
139. # Ход игрока
140. drawBoard (theBoard)
141. move = getPlayerMove (theBoard)
142.makeMove (theBoard, playerLetter, move)
143.
144. if isWinner (theBoard, playerLetter):
145. drawBoard (theBoard)
146. print ('Ура! Вы выиграли игру!')
147. gameIsPlaying = Ложь
148. else:
149. if isBoardFull (theBoard):
150. drawBoard (theBoard)
151. print («Игра - ничья!»)
152. break
153.else:
154. turn = 'computer'
155.
156. else:
157. # Ход компьютера
158. move = getComputerMove (theBoard, computerLetter)
159. makeMove (theBoard, computerLetter, move)
160.
161. if isWinner (theBoard, computerLetter):
162. drawBoard (theBoard)
163. print («Компьютер победил вас! Вы проиграли.»)
164. gameIsPlaying = False
165.else:
166. if isBoardFull (theBoard):
167. drawBoard (theBoard)
168. print («Игра - ничья!»)
169. break
170. else:
171. turn = 'player'
172.
173. print ('Вы хотите сыграть снова? (Да или нет)')
174. если не input (). Lower (). Startwith ('y'):
175. break

Разработка программы

На рис. 10-2 показана блок-схема программы «Крестики-нолики».Программа начинается с того, что игроку предлагается выбрать букву: X или O . Кто ходит первым, выбирается случайным образом. Затем игрок и компьютер по очереди делают ходы.

Рисунок 10-2: Блок-схема для Tic-Tac-Toe

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

Представление платы в виде данных

Во-первых, вы должны выяснить, как представить доску как данные в переменной. На бумаге доска Tic-Tac-Toe нарисована как пара горизонтальных линий и пара вертикальных линий с X , O или пустым пространством в каждом из девяти пространств.

В программе доска для крестиков-ноликов представлена ​​в виде списка строк, как в ASCII-искусстве Палача.Каждая строка представляет собой одно из девяти пробелов на доске. Строки представляют собой либо «X» для проигрывателя X , либо «O» для проигрывателя O , либо «один пробел» для пустого места.

Помните, что мы раскладываем нашу доску как цифровую клавиатуру. Таким образом, если список из 10 строк был сохранен в переменной с именем board, тогда доска [7] была бы верхним левым пространством на доске, доска [8] была бы верхним-средним пространством, доска [9] была бы верхний правый пробел и т. д. Программа игнорирует строку с индексом 0 в списке.Игрок вводит число от 1 до 9, чтобы сообщить игре, на какое место он хочет перейти.

Разработка стратегии с помощью ИИ игры

ИИ должен иметь возможность смотреть на доску и решать, по каким типам ячеек он будет перемещаться. Для ясности обозначим три типа пространств на доске Tic-Tac-Toe: углы, стороны и центр. На диаграмме на рис. 10-3 показано, что представляет собой каждое пространство.

Стратегия ИИ для игры в крестики-нолики будет следовать простому алгоритму - конечной серии инструкций для вычисления результата.Одна программа может использовать несколько разных алгоритмов. Алгоритм можно представить в виде блок-схемы. Алгоритм ИИ «крестики-нолики» вычислит лучший ход, который нужно сделать, как показано на рисунке 10-4.

Рисунок 10-3: Расположение боковых, угловых и центральных пространств

Рис. 10-4. Прямоугольники представляют пять шагов алгоритма «Получить ход компьютера». Стрелки, указывающие влево, переходят к окну «Проверить, выиграл ли компьютер».

Алгоритм AI состоит из следующих шагов:

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

  2. Посмотрите, может ли игрок сделать ход, из-за которого компьютер проиграет игру. Если есть, переместитесь туда, чтобы заблокировать игрока. В противном случае переходите к шагу 3.

  3. Проверьте, свободны ли какие-либо угловые ячейки (ячейки 1, 3, 7 или 9). Если так, переезжайте туда. Если свободного места в углу нет, переходите к шагу 4.

  4. Проверить, свободен ли центр. Если так, переезжайте туда. Если нет, переходите к шагу 5.

  5. Переместитесь на любую из боковых клеток (клетки 2, 4, 6 или 8). Больше нет шагов, потому что боковые пробелы - это все, что осталось, если выполнение достигнет шага 5.

Все это происходит в поле "Получить компьютер" на блок-схеме на рисунке 10-2. Вы можете добавить эту информацию к блок-схеме с помощью полей на рис. 10-4.

Этот алгоритм реализован в getComputerMove () и других функциях, которые вызывает getComputerMove ().

Импорт случайного модуля

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

1. # Крестики-нолики
2.
3. Импорт случайный

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

Печать платы на экране

В следующей части кода мы определяем функцию для рисования платы:

5.def drawBoard (board):
6. # Эта функция распечатывает доску, которую она передала.
7.
8. # "board" - это список из 10 строк, представляющих доску (игнорируйте
индекс 0).
9. печать (доска [7] + '|' + доска [8] + '|' + доска [9])
10. печать ('- + - + -')
11. печать (доска [4] + '|' + доска [5] + '|' + доска [6])
12. print ('- + - + -')
13. print (доска [1] + '|' + доска [2] + '|' + доска [3])

Функция drawBoard () печатает игровое поле, представленное параметром board.Помните, что доска представлена ​​в виде списка из 10 строк, где строка с индексом 1 - это отметка в пространстве 1 на доске для крестиков-ноликов и так далее. Строка с индексом 0 игнорируется. Многие функции игры работают, передавая список из 10 строк в качестве доски.

Убедитесь, что расстояние в струнах выбрано правильно; в противном случае доска будет выглядеть забавно при печати на экране. Вот несколько примеров вызовов (с аргументом для доски) drawBoard () и того, что функция будет печатать.

>>> drawBoard (['', '', '', '', 'X', 'O', '', 'X', '', 'O'])
X | |
- + - + -
X | O |
- + - + -
| |
>>> drawBoard (['', '', '' ',' ',' ',' ',' ',' ',' ',' '])
| |
- + - + -
| |
- + - + -
| |

Программа берет каждую строку и размещает ее на доске в порядке номеров в соответствии с цифровой клавиатурой на рисунке 10-1, так что первые три строки являются нижним рядом платы, следующие три строки - средним, а последние три струны являются верхними.

Предоставление игроку возможности выбрать X или O

Затем мы определим функцию для присвоения проигрывателю X или O :

15. def inputPlayerLetter ():
16. # Позволяет игроку ввести букву, которой он хочет быть.
17. # Возвращает список, в котором буква игрока является первым элементом, а буква компьютера
- вторым.
18. letter = ''
19. пока нет (буква == 'X' или буква == 'O'):
20.print ('Вы хотите быть X или O?')
21. letter = input (). upper ()

Функция inputPlayerLetter () спрашивает, хочет ли игрок быть X или O . Условие цикла while содержит круглые скобки, что означает, что выражение внутри скобок вычисляется первым. Если бы буквенная переменная была установлена ​​на 'X', выражение будет оцениваться следующим образом:

Если буква имеет значение «X» или «O», то условие цикла - False и позволяет продолжению выполнения программы после блока while.Если условие истинно, программа будет продолжать просить игрока выбрать букву, пока игрок не введет X или O . Строка 21 автоматически изменяет строку, возвращаемую вызовом input (), на прописные буквы с помощью строкового метода upper ().

Следующая функция возвращает список с двумя элементами:

23. # Первый элемент в списке - буква игрока; второй -
буква компьютера.
24. Если буква == 'X':
25.return ['X', 'O']
26. else:
27. return ['O', 'X']

Первый элемент (строка с индексом 0) - это буква игрока, а второй элемент (строка с индексом 1) - это буква компьютера. Операторы if и else выбирают соответствующий список для возврата.

Решаем, кто пойдет первым

Затем мы создаем функцию, которая использует randint (), чтобы выбрать, играет ли игрок или компьютер первым:

29. def whoGoesFirst ():
30.# Случайным образом выбрать, какой игрок ходит первым.
31. if random.randint (0, 1) == 0:
32. return 'computer'
33. else:
34. return 'player'

Функция whoGoesFirst () выполняет виртуальный подбрасывание монеты, чтобы определить, идет ли первым игрок или компьютер. Подбрасывание монеты выполняется с помощью вызова random.randint (0, 1). Существует 50-процентная вероятность того, что функция вернет 0, и 50-процентная вероятность того, что функция вернет 1. Если этот вызов функции возвращает 0, функция whoGoesFirst () возвращает строку «компьютер».В противном случае функция возвращает строку player. Код, вызывающий эту функцию, будет использовать возвращаемое значение, чтобы определить, кто сделает первый ход в игре.

Размещение отметки на доске

Функция makeMove () проста:

36. def makeMove (доска, буква, ход):
37. доска [движение] = буква

Параметры: доска, буква и ход. Переменная доска - это список из 10 строк, который представляет состояние доски.Переменная буква - это буква игрока («X» или «O»). Переменный ход - это место на доске, куда игрок хочет пойти (целое число от 1 до 9).

Но подождите - в строке 37 этот код, кажется, меняет один из элементов в списке доски на буквенное значение. Однако, поскольку этот код находится в функции, параметр платы будет забыт, когда функция вернется. Так не следует ли забыть и об изменении платы?

На самом деле это не так, потому что списки являются особенными, когда вы передаете их в качестве аргументов функциям.Фактически вы передаете в список ссылку , а не сам список. Давайте узнаем о разнице между списками и ссылками на списки.

Список литературы

Введите в интерактивную оболочку следующее:

>>> спам = 42
>>> сыр = спам
>>> спам = 100
>>> спам
100
>>> сыр
42

Эти результаты имеют смысл исходя из того, что вы знаете до сих пор.Вы присваиваете 42 переменной спама, а затем присваиваете значение в спаме переменной сыр. Когда вы позже перезапишете спам на 100, это не повлияет на ценность сыра. Это потому, что спам и сыр - разные переменные, которые хранят разные значения.

Но списки так не работают. Когда вы назначаете список переменной, вы фактически назначаете этой переменной ссылку на список. Ссылка - это значение, указывающее на место, где хранится некоторый бит данных. Давайте посмотрим на код, который упростит понимание.Введите это в интерактивную оболочку:

➊ >>> spam = [0, 1, 2, 3, 4, 5]
➋ >>> cheese = spam
➌ >>> cheese [1] = 'Hello!'
>>> spam
[0, 'Привет!', 2, 3, 4, 5]
>>> сыр
[0, 'Привет!', 2, 3, 4, 5]

Код изменил только список сыров, но похоже, что изменились и сыры, и спам-списки. Это связано с тем, что переменная спама не содержит самого значения списка, а скорее является ссылкой на список, как показано на рисунке 10-5.Сам список не содержится ни в одной переменной, а существует вне их.

Рисунок 10-5: Список спама , созданный на . Переменные хранят не списки, а ссылки на списки.

Обратите внимание, что cheese = spam копирует ссылку на список из спама в cheese ➋ вместо копирования самого значения списка. Теперь и спам, и сыр хранят ссылку, которая ссылается на одно и то же значение списка. Но список только один, потому что сам список не копировался.Рисунок 10-6 показывает это копирование.

Рисунок 10-6. Переменные spam и cheese хранят две ссылки на один и тот же список.

Итак, сыр [1] = 'Привет!' Строка ➌ изменяет тот же список, на который ссылается спам. Вот почему спам возвращает то же значение списка, что и сыр. У них обоих есть ссылки, которые относятся к одному и тому же списку, как показано на рисунке 10-7.

Рисунок 10-7: При изменении списка изменяются все переменные со ссылками на этот список.

Если вы хотите, чтобы спам и сыр сохраняли два разных списка, вам нужно создать два списка вместо копирования ссылки:

>>> spam = [0, 1, 2, 3, 4, 5]
>>> сыр = [0, 1, 2, 3, 4, 5]

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

>>> spam = [0, 1, 2, 3, 4, 5]
>>> cheese = [0, 1, 2, 3, 4, 5]
>>> cheese [1] = 'Привет! '
>>> спам
[0, 1, 2, 3, 4, 5]
>>> сыр
[0, 'Hello!', 2, 3, 4, 5]

На рис. 10-8 показано, как в этом примере настраиваются переменные и значения списка.

Словари работают точно так же. Переменные не хранят словари; в них хранится ссылок к словарям.

Рисунок 10-8: Переменные spam и cheese теперь хранят ссылки на два разных списка.

Использование списков ссылок в makeMove ()

Вернемся к функции makeMove ():

36. def makeMove (доска, буква, ход):
37. доска [движение] = буква

Когда значение списка передается для параметра платы, локальная переменная функции на самом деле является копией ссылки на список, а не копией самого списка.Таким образом, любые изменения платы в этой функции также будут внесены в исходный список. Несмотря на то, что board является локальной переменной, функция makeMove () изменяет исходный список.

Параметры letter и move являются копиями передаваемых вами строковых и целочисленных значений. Поскольку они являются копиями значений, если вы измените букву или переместите в этой функции, исходные переменные, которые вы использовали при вызове makeMove (), не изменятся.

Проверка, выиграл ли игрок

Строки с 42 по 49 в функции isWinner () на самом деле представляют собой один длинный оператор возврата:

39.def isWinner (bo, le):
40. # Учитывая доску и букву игрока, эта функция возвращает True, если
этот игрок выиграл.
41. # Мы используем «bo» вместо «board» и «le» вместо «letter», поэтому нам не нужно набирать столько текста.
42. return ((bo [7] == le и bo [8] == le и bo [9] == le) или # В верхней части

43. (bo [4] == le и bo [ 5] == le и bo [6] == le) или # через середину
44. (bo [1] == le и bo [2] == le и bo [3] == le) или # через нижняя
45.(bo [7] == le и bo [4] == le и bo [1] == le) или # вниз по левой стороне
46. (bo [8] == le и bo [5] == le and bo [2] == le) или # вниз по центру
47. (bo [9] == le и bo [6] == le и bo [3] == le) или # вниз по правой стороне

48. (bo [7] == le и bo [5] == le и bo [3] == le) или # Diagonal
49. (bo [9] == le и bo [5] == le и bo [1] == le)) # Диагональ

Имена файлов и файлов являются ярлыками для параметров платы и букв. Эти более короткие имена означают, что вам нужно меньше вводить в эту функцию.Помните, Python не волнует, как вы называете свои переменные.

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

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

Давайте представим, что le - это 'O', а bo - это ['', 'O', 'O', 'O', '' ',' X ',' ',' X ',' ',' ']. Плата будет выглядеть так:

X | |
- + - + -
| X |
- + - + -
O | O | O

Вот как будет оцениваться выражение после ключевого слова return в строке 42. Сначала Python заменяет переменные bo и le значениями в каждой переменной:

return (('X' == 'O' и '' == 'O' и '' == 'O') или
('' == 'O' и 'X' == 'O' и ' '==' O ') или
(' O '==' O 'и' O '==' O 'и' O '==' O ') или
(' X '==' O 'и' '==' O 'и' O '==' O ') или
(' '==' O 'и' X '==' O 'и' O '==' O ') или
(' ' == 'O' и '' == 'O' и 'O' == 'O') или
('X' == 'O' и 'X' == 'O' и 'O' == ' O ') или
(' '==' O 'и' X '==' O 'и' O '==' O '))

Затем Python оценивает все эти == сравнения внутри скобок с логическими значениями:

return ((Ложь и Ложь и Ложь) или
(Ложь и Ложь и Ложь) или
(Истина и Истина и Истина) или
(Ложь, Ложь и Истина) или
(Ложь, Ложь и Истина) или
(Ложь и Ложь и Истина) или
(Ложь, Ложь и Истина) или
(Ложь, Ложь и Истина))

Затем интерпретатор Python вычисляет все выражения в круглых скобках:

return ((False) или
(False) или
(True) или
(False) или
(False) или
(False) или
(False) или
(False))

Поскольку теперь внутри каждой из внутренних круглых скобок есть только одно значение, вы можете избавиться от них:

возврат (Ложь, или
Ложь, или
Ложь, или
Ложь, или
Ложь, или
Ложь, или
Ложь, или
Ложь)

Теперь Python вычисляет выражение, связанное всеми операторами or:

возврат (True)

Еще раз избавьтесь от скобок, и у вас останется одно значение:

возврат True

Итак, с учетом этих значений для bo и le выражение будет иметь значение True.Таким образом программа может определить, выиграл ли один из игроков игру.

Дублирование данных платы

Функция getBoardCopy () позволяет легко сделать копию заданного 10-строчного списка, который представляет доску для крестиков-ноликов в игре.

51. def getBoardCopy (board):
52. # Сделайте копию списка досок и верните ее.
53. boardCopy = []
54. для i in board:
55. boardCopy.append (i)
56. return boardCopy

Когда алгоритм ИИ планирует свои ходы, ему иногда необходимо внести изменения во временную копию доски, не меняя саму доску.В таких случаях мы вызываем эту функцию, чтобы сделать копию списка доски. Новый список создается в строке 53.

Прямо сейчас список, хранящийся в boardCopy, является просто пустым списком. Цикл for будет перебирать параметр платы, добавляя копию строковых значений с фактической платы к дублированной доске. После того, как функция getBoardCopy () создает копию реальной платы, она возвращает ссылку на эту новую доску в boardCopy, а не на исходную в board.

Проверка наличия свободного места на плате

Учитывая доску крестики-нолики и возможный ход, простая функция isSpaceFree () возвращает, доступен ли этот ход или нет:

58.def isSpaceFree (board, move):
59. # Вернуть True, если пройденный ход свободен на переданной доске.
60. вернуть доску [move] == ''

Помните, что свободные места в списках досок помечаются как одинарная строка. Если элемент в индексе пробела не равен "", то пробел занят.

Разрешение игроку сделать ход

Функция getPlayerMove () просит игрока ввести номер клетки, на которую он хочет переместиться:

62.def getPlayerMove (board):
63. # Позвольте игроку ввести свой ход.
64. move = ''
65. while move not in '1 2 3 4 5 6 7 8 9'. Split () или нет
isSpaceFree (board, int (move)):
66. print ('What is ваш следующий ход? (1-9) ')
67. move = input ()
68. return int (move)

Условие в строке 65 истинно, если любое из выражений слева или справа от оператора или истинно. Цикл гарантирует, что выполнение не будет продолжено, пока игрок не введет целое число от 1 до 9.Он также проверяет, что введенное пространство еще не занято, учитывая, что доска Tic-Tac-Toe передана в функцию для параметра board. Две строки кода внутри цикла while просто просят игрока ввести число от 1 до 9.

Выражение в левой части проверяет, равен ли ход игрока «1», «2», «3» и т. Д. До «9» путем создания списка с этими строками (с помощью метода split ()) и проверка, есть ли ход в этом списке. В этом выражении «1 2 3 4 5 6 7 8 9».split () оценивается как ['1', '2', '3', '4', '5', '6', '7', '8', '9'], но первое легче ввести .

Выражение справа проверяет, является ли введенный игроком ход свободным местом на доске, вызывая isSpaceFree (). Помните, что isSpaceFree () возвращает True, если переданный вами ход доступен на доске. Обратите внимание, что isSpaceFree () ожидает целое число для перемещения, поэтому функция int () возвращает целочисленную форму перемещения.

Операторы not добавляются к обеим сторонам, так что условие истинно, когда любое из этих требований не выполняется.Это заставляет цикл снова и снова запрашивать у игрока число, пока он не сделает правильный ход.

Наконец, строка 68 возвращает целочисленную форму любого хода, сделанного игроком. input () возвращает строки, поэтому функция int () вызывается для возврата целочисленной формы строки.

Оценка короткого замыкания

Возможно, вы заметили возможную проблему в функции getPlayerMove (). Что, если игрок ввел «Z» или другую нецелую строку? Выражение переместить не в '1 2 3 4 5 6 7 8 9'.split () с левой стороны от или вернет False, как ожидалось, а затем Python оценит выражение с правой стороны от оператора или.

Но вызов int ('Z') приведет к тому, что Python выдаст ошибку, потому что функция int () может принимать только строки числовых символов, такие как '9' или '0', а не строки, такие как 'Z'.

Чтобы увидеть пример такого рода ошибки, введите в интерактивную оболочку следующее:

>>> int ('42 ')
42
>>> int (' Z ')
Traceback (последний вызов последним):
Файл «», строка 1, в
int ('Z')
ValueError: недопустимый литерал для int () с базой 10: 'Z'

Но когда вы играете в крестики-нолики и пытаетесь ввести «Z» для своего хода, этой ошибки не возникает.Это связано с тем, что условие цикла while замыкается накоротко.

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

>>> def ReturnsTrue ():
print ('Вызывается ReturnsTrue ().')
return True
>>> def ReturnsFalse ():
print ('Вызывается ReturnsFalse ().')
return False
>>> ReturnsTrue ()
ReturnsTrue () был вызван.
True
>>> ReturnsFalse ()
ReturnsFalse () был вызван.
Ложь

При вызове ReturnsTrue () выводится сообщение «ReturnsTrue () was called». а затем также отображает возвращаемое значение ReturnsTrue (). То же самое и с ReturnsFalse ().

Теперь введите в интерактивную оболочку следующее:

>>> ReturnsFalse () или ReturnsTrue ()
Вызван ReturnsFalse ().
ReturnsTrue () был вызван.
True
>>> ReturnsTrue () или ReturnsFalse ()
ReturnsTrue () был вызван.
Истинно

Первая часть имеет смысл: выражение ReturnsFalse () или ReturnsTrue () вызывает обе функции, поэтому вы видите оба напечатанных сообщения.

Но второе выражение показывает только «ReturnsTrue () was called.», А не «ReturnsFalse () was called.». Это потому, что Python вообще не вызывал ReturnsFalse (). Поскольку левая часть оператора or - True, не имеет значения, что возвращает ReturnsFalse (), поэтому Python не вызывает его.Оценка была замкнута.

То же самое относится к оператору и. Теперь введите в интерактивную оболочку следующее:

>>> ReturnsTrue () и ReturnsTrue ()
ReturnsTrue () был вызван.
ReturnsTrue () был вызван.
True
>>> ReturnsFalse () и ReturnsFalse ()
ReturnsFalse () был вызван.
Ложь

Опять же, если левая часть оператора and имеет значение False, тогда все выражение равно False. Не имеет значения, является ли правая часть и истинной или ложной, поэтому Python не утруждает себя ее оценкой.И False, и True, и False и False оцениваются как False, поэтому Python сокращает оценку.

Вернемся к строкам с 65 по 68 программы «Крестики-нолики»:

65. пока ход не в '1 2 3 4 5 6 7 8 9'. Split () или нет
isSpaceFree (board, int (move)):
66. print ('Каким будет ваш следующий ход? (1- 9) ')
67. move = input ()
68. return int (move)

Так как часть условия в левой части оператора or (переместить не в '1 2 3 4 5 6 7 8 9'.split ()) оценивается как True, интерпретатор Python знает, что все выражение будет оцениваться как True. Не имеет значения, имеет ли выражение справа от or значение True или False, потому что только одно значение с обеих сторон от оператора or должно иметь значение True, чтобы все выражение было истинным.

Итак, Python перестает проверять остальную часть выражения и даже не пытается оценить часть not isSpaceFree (board, int (move)). Это означает, что функции int () и isSpaceFree () никогда не вызываются, пока move не находится в '1 2 3 4 5 6 7 8 9'.split () имеет значение True.

Это хорошо работает для программы, потому что, если правая часть условия истинна, то move не является строкой однозначного числа. Это приведет к тому, что int () выдаст нам ошибку. Но если move не находится в '1 2 3 4 5 6 7 8 9'. Split () оценивается как True, короткие замыкания Python не isSpaceFree (board, int (move)), а int (move) не вызывается.

Выбор хода из списка ходов

Теперь давайте посмотрим на функцию chooseRandomMoveFromList (), которая будет полезна для кода AI позже в программе:

70.def chooseRandomMoveFromList (board, moveList):
71. # Возвращает допустимый ход из переданного списка на переданной доске.
72. # Не возвращает None, если нет допустимого хода.
73. possibleMoves = []
74. for i in moveList:
75. if isSpaceFree (board, i):
76. possibleMoves.append (i)

Помните, что параметр доски - это список строк, представляющий доску для крестиков-ноликов. Второй параметр, moveList, представляет собой список целых чисел возможных пробелов, из которых можно выбрать.Например, если moveList равен [1, 3, 7, 9], это означает, что chooseRandomMoveFromList () должен возвращать целое число для одного из угловых пространств.

Однако, chooseRandomMoveFromList () сначала проверяет, достаточно ли места для перехода. Список possibleMoves начинается с пустого списка. Затем цикл for выполняет итерацию по moveList. Движения, которые заставляют isSpaceFree () возвращать True, добавляются в possibleMoves с помощью метода append ().

На данный момент в списке possibleMoves есть все ходы, которые были в moveList, которые также являются свободными пробелами.Затем программа проверяет, пуст ли список:

78. if len (possibleMoves)! = 0:
79. return random.choice (possibleMoves)
80. else:
81. return Нет

Если список не пуст, значит, на доске можно сделать хотя бы один ход.

Но этот список может быть пустым. Например, если moveList был [1, 3, 7, 9], но на доске, представленной параметром board, все угловые ячейки уже заняты, список possibleMoves будет [].В этом случае len (possibleMoves) оценивается как 0, и функция возвращает значение None.

Значение «Нет»

Значение None означает отсутствие значения. None - единственное значение типа данных NoneType. Вы можете использовать значение None, когда вам нужно значение, которое означает «не существует» или «ничего из вышеперечисленного».

Например, предположим, что у вас есть переменная с именем quizAnswer, в которой хранится ответ пользователя на некоторый верный / ложный вопрос викторины. Переменная может содержать True или False для ответа пользователя.Но если пользователь не ответил на вопрос, вы не захотите установить quizAnswer на True или False, потому что тогда это будет выглядеть так, как будто пользователь ответил на вопрос. Вместо этого вы можете установить quizAnswer на None, если пользователь пропустил вопрос.

В качестве примечания, None не отображается в интерактивной оболочке, как другие значения:

>>> 2 + 2
4
>>> 'Это строковое значение.'
'Это строковое значение.'
>>> Нет
>>>

Значения первых двух выражений печатаются как выходные данные в следующей строке, но None не имеет значения, поэтому не печатается.

Функции, которые ничего не возвращают, на самом деле возвращают значение None. Например, print () возвращает None:

>>> spam = print ('Привет, мир!')
Привет, мир!
>>> спам == Нет
Верно

Здесь мы назначили print («Hello world!») Как спам. Функция print (), как и все функции, имеет возвращаемое значение. Несмотря на то, что print () печатает вывод, вызов функции возвращает None. IDLE не отображает «Нет» в интерактивной оболочке, но вы можете сказать, что для спама установлено значение «Нет», поскольку spam == None оценивается как «Истина».

Создание искусственного интеллекта компьютера

Функция getComputerMove () содержит код ИИ:

83. def getComputerMove (board, computerLetter):
84. # Учитывая доску и букву компьютера, определите, куда переместить
, и верните этот ход.
85. if computerLetter == 'X':
86. playerLetter = 'O'
87. else:
88. playerLetter = 'X'

Первый аргумент - это доска для крестиков-ноликов для параметра платы.Второй аргумент - это буква, которую использует компьютер: «X» или «O» в параметре computerLetter. Первые несколько строк просто присваивают другую букву переменной с именем playerLetter. Таким образом, можно использовать один и тот же код вне зависимости от того, X или O компьютера.

Вспомните, как работает алгоритм ИИ крестики-нолики:

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

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

  3. Проверьте, свободны ли какие-либо углы (клетки 1, 3, 7 или 9). Если свободного места в углу нет, переходите к шагу 4.

  4. Проверить, свободен ли центр. Если так, переезжайте туда. Если это не так, переходите к шагу 5.

  5. Двигайтесь по любой из сторон (клетки 2, 4, 6 или 8). Больше нет шагов, потому что боковые пробелы - единственные оставшиеся пробелы, если выполнение дошло до этого шага.

Функция вернет целое число от 1 до 9, представляющее ход компьютера. Давайте рассмотрим, как каждый из этих шагов реализован в коде.

Проверка, может ли компьютер выиграть одним движением

Прежде всего, если компьютер может выиграть следующим ходом, он должен немедленно сделать этот выигрышный ход.

90. # Вот алгоритм для нашего ИИ "крестики-нолики":
91. # Сначала проверьте, сможем ли мы выиграть следующим ходом.
92. for i in range (1, 10):
93. boardCopy = getBoardCopy (board)
94. if isSpaceFree (boardCopy, i):
95. makeMove (boardCopy, computerLetter, i)
96. if isWinner ( boardCopy, computerLetter):
97. return i

Цикл for, который начинается в строке 92, перебирает все возможные шаги от 1 до 9. Код внутри цикла имитирует то, что произошло бы, если бы компьютер сделал это движение.

Первая строка в цикле (строка 93) создает копию списка плат.Это сделано для того, чтобы смоделированное движение внутри цикла не изменяло реальную доску Tic-Tac-Toe, хранящуюся в переменной board. GetBoardCopy () возвращает идентичное, но отдельное значение списка плат.

Строка 94 проверяет, свободно ли место, и, если да, имитирует перемещение по копии доски. Если этот ход приводит к выигрышу компьютера, функция возвращает целое число этого хода.

Если ни одно из пробелов не приводит к выигрышу, цикл завершается, и выполнение программы продолжается до строки 100.

Проверка того, может ли игрок выиграть одним движением

Далее код будет имитировать человека-игрока, перемещающегося по каждой из ячеек:

99. # Проверьте, может ли игрок выиграть на следующем ходу, и заблокируйте его.
100. for i in range (1, 10):
101. boardCopy = getBoardCopy (board)
102. if isSpaceFree (boardCopy, i):
103. makeMove (boardCopy, playerLetter, i)
104. if isWinner ( boardCopy, playerLetter):
105.возврат я

Код аналогичен циклу в строке 92, за исключением того, что буква игрока помещается на копию доски. Если функция isWinner () показывает, что игрок выиграет ходом, компьютер вернет тот же ход, чтобы этого не произошло.

Если игрок-человек не может выиграть еще одним ходом, цикл for завершается, и выполнение продолжается до строки 108.

Проверка углового, центрального и бокового промежутков (в указанном порядке)

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

Компьютер сначала пытается переместиться в одно из угловых мест:

107. # Попробуйте взять один из углов, если они свободны.
108. move = chooseRandomMoveFromList (board, [1, 3, 7, 9])
109. if move! = None:
110. return move

Вызов функции chooseRandomMoveFromList () со списком [1, 3, 7, 9] гарантирует, что функция вернет целое число для одного из угловых пространств: 1, 3, 7 или 9.

Если все угловые пробелы заняты, функция chooseRandomMoveFromList () возвращает None, и выполнение переходит к строке 113:

112.# Попробуйте взять центр, если он бесплатный.
113. if isSpaceFree (доска, 5):
114. return 5

Если ни один из углов не доступен, линия 114 перемещается по центру, если она свободна. Если центральное пространство занято, выполнение переходит к строке 117:

.

116. # Двигайтесь по одной из сторон.
117. вернуть chooseRandomMoveFromList (board, [2, 4, 6, 8])

Этот код также вызывает selectRandomMoveFromList (), за исключением того, что вы передаете ему список боковых пробелов: [2, 4, 6, 8].Эта функция не вернет None, потому что боковые пробелы - единственные пробелы, которые можно оставить. На этом заканчивается функция getComputerMove () и алгоритм AI.

Проверка, заполнена ли плата

Последняя функция isBoardFull ():

119. def isBoardFull (board):
120. # Вернуть True, если все места на доске были заняты. В противном случае
вернет False.
121. для i в диапазоне (1, 10):
122. if isSpaceFree (board, i):
123.return False
124. return True

Эта функция возвращает True, если 10-строчный список в переданном ей аргументе доски содержит «X» или «O» в каждом индексе (кроме индекса 0, который игнорируется). Цикл for позволяет нам проверять индексы с 1 по 9 в списке доски. Как только он находит свободное место на доске (то есть, когда isSpaceFree (board, i) возвращает True), функция isBoardFull () вернет False.

Если выполнение проходит через каждую итерацию цикла, то ни одно из пробелов не является свободным.Строка 124 затем выполнит return True.

Игровой цикл

Строка 127 - это первая строка, которая не находится внутри функции, поэтому это первая строка кода, которая выполняется при запуске этой программы.

127. print («Добро пожаловать в крестики-нолики!»)

Эта строка приветствует игрока перед началом игры. Затем программа входит в цикл while в строке 129:

.

129. пока True:
130. # Сбросить плату.
131. theBoard = [''] * 10

Цикл while продолжает цикл до тех пор, пока выполнение не встретит оператор break.Строка 131 устанавливает основную доску для крестиков-ноликов в переменной с именем theBoard. Доска начинается пустой, что мы представляем списком из 10 строк с одним пробелом. Вместо того, чтобы вводить полный список, в строке 131 используется репликация списка. [''] * 10 короче, чем ['', '', '', '', '', '', '', '', '', ''].

Выбор марки игрока и кто пойдет первым

Затем функция inputPlayerLetter () позволяет игроку указать, хочет ли он быть X или O :

132.playerLetter, computerLetter = inputPlayerLetter ()

Функция возвращает список из двух строк: ['X', 'O'] или ['O', 'X']. Мы используем множественное присваивание, чтобы установить playerLetter для первого элемента в возвращаемом списке и computerLetter для второго.

Отсюда функция whoGoesFirst () случайным образом решает, кто идет первым, возвращая либо строку «player», либо строку «computer», а затем строка 134 сообщает игроку, кто пойдет первым:

133. Turn = whoGoesFirst ()
134.print ('+ поворот +' будет первым. ')
135. gameIsPlaying = True

Переменная gameIsPlaying отслеживает, продолжается ли игра до сих пор, выиграл ли кто-то или сыграл вничью.

Выполнение хода игрока

Цикл в строке 137 будет продолжаться между кодом хода игрока и ходом компьютера, пока для gameIsPlaying установлено значение True:

137. while gameIsPlaying:
138. if turn == 'player':
139.# Ход игрока
140. drawBoard (theBoard)
141. move = getPlayerMove (theBoard)
142. makeMove (theBoard, playerLetter, move)

Переменная хода изначально была установлена ​​на «player» или «computer» вызовом whoGoesFirst () в строке 133. Если Turn равен «computer», то условие строки 138 - False, и выполнение переходит к строке 156.

Но если строка 138 оценивается как True, строка 140 вызывает drawBoard () и передает переменную theBoard для печати доски Tic-Tac-Toe на экране.Затем getPlayerMove () позволяет игроку ввести свой ход (а также гарантирует, что это правильный ход). Функция makeMove () добавляет на доску X или O игрока.

Теперь, когда игрок сделал свой ход, программа должна проверить, выиграл ли он игру:

144. if isWinner (theBoard, playerLetter):
145. drawBoard (theBoard)
146. print («Ура! Вы выиграли игру!»)
147. gameIsPlaying = False

Если функция isWinner () возвращает True, код блока if отображает выигрышную доску и печатает сообщение, сообщающее игроку, что они выиграли.Переменная gameIsPlaying также имеет значение False, чтобы выполнение не продолжалось до очереди компьютера.

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

148. else:
149. if isBoardFull (theBoard):
150. drawBoard (theBoard)
151. print («Игра - ничья!»)
152.перерыв

В этом блоке else функция isBoardFull () возвращает True, если больше нет ходов, которые нужно сделать. В этом случае блок if, начинающийся со строки 149, отображает ничью и сообщает игроку, что произошла ничья. Затем выполнение прерывается из цикла while и переходит к строке 173.

Если игрок не выиграл или не сыграл вничью, программа вводит другой оператор else:

153. else:
154. turn = 'computer'

Строка 154 устанавливает переменную поворота на «компьютер», чтобы программа выполняла код для включения компьютера на следующей итерации.

Запуск компьютера

Если переменная хода не была «player» для условия в строке 138, то это должна быть очередь компьютера. Код в этом блоке else аналогичен коду хода игрока:

156. else:
157. # Ход компьютера
158. move = getComputerMove (theBoard, computerLetter)
159. makeMove (theBoard, computerLetter, move)
160.
161. if isWinner (theBoard, computerLetter):
162.drawBoard (theBoard)
163. print («Компьютер победил вас! Вы проигрываете.»)
164. gameIsPlaying = False
165. else:
166. if isBoardFull (theBoard):
167. drawBoard (theBoard)
168 . Print («Игра - ничья!»)
169. break
170. else:
171. turn = 'player'

Строки с 157 по 171 почти идентичны коду хода игрока в строках с 139 по 154.Единственная разница в том, что этот код использует букву компьютера и вызывает getComputerMove ().

Если игра не была выиграна или ничья, наборы строки 171 переходят в очередь игрока. Внутри цикла while больше нет строк кода, поэтому выполнение возвращается к оператору while в строке 137.

Просить игрока сыграть снова

Наконец, программа спрашивает игрока, хотят ли они сыграть в другую игру:

173. print («Ты хочешь снова сыграть? (Да или нет)»)
174.если не input (). lower (). playswith ('y'):
175. break

Строки с 173 по 175 выполняются сразу после блока while, запущенного оператором while в строке 137. gameIsPlaying имеет значение False, когда игра закончилась, поэтому в этот момент игра спрашивает игрока, хотят ли они играть снова.

Выражение not input (). Lower (). Playswith ('y') будет иметь значение True, если игрок вводит все, что не начинается с 'y'. В этом случае выполняется инструкция break. Это прерывает выполнение цикла while, который был запущен в строке 129.Но поскольку после этого блока while больше нет строк кода, программа завершается и игра завершается.

Сводка

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

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

Ключом к реализации нашего ИИ является создание копий данных доски и имитация движений на копии. Таким образом, код AI может видеть, приводит ли ход к выигрышу или проигрышу. Тогда ИИ сможет сделать этот ход на реальной доске. Этот тип моделирования эффективен при прогнозировании того, что является хорошим, а что нет.

Tac-Toe - Введение CS50 в искусственный интеллект с Python

Используя Minimax, внедрите ИИ для оптимальной игры в крестики-нолики.

Когда это делать

К 2021 году 1231T235900-0500 .

Как получить помощь

  1. Задавайте вопросы через Эда!
  2. Задавайте вопросы в любом из сообществ CS50!

Начало работы

Понимание

В этом проекте два основных файла: runner.py и tictactoe.py . tictactoe.py содержит всю логику для игры и для совершения оптимальных ходов. runner.py был реализован для вас и содержит весь код для запуска графического интерфейса игры. Выполнив все необходимые функции в tictactoe.py , вы сможете запустить python runner.py , чтобы играть против своего ИИ!

Давайте откроем tictactoe.py , чтобы понять, что предоставляется.Сначала мы определяем три переменные: X , O и EMPTY , чтобы представить возможные ходы доски.

Функция initial_state возвращает начальное состояние платы. Для этой проблемы мы выбрали представление платы в виде списка из трех списков (представляющих три строки платы), где каждый внутренний список содержит три значения: X , O или EMPTY . .
Ниже перечислены функции, которые мы оставили на ваше усмотрение!

Спецификация

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

Завершите реализации player , действий , result , Winner , terminal , utility и minimax .

  • Функция player должна принимать в качестве входных данных состояние доски и возвращать, какой сейчас ход игрока ( X или O ).
    • В исходном состоянии игры X получает первый ход. Впоследствии игрок чередует каждый дополнительный ход.
    • Приемлемо любое возвращаемое значение, если в качестве входных данных используется клеммная колодка (т. Е. Игра уже окончена).
  • Действия Функция должна возвращать набор всех возможных действий, которые могут быть выполнены на данной плате.
    • Каждое действие должно быть представлено в виде кортежа (i, j) , где i соответствует строке хода ( 0 , 1 или 2 ), а j соответствует какой ячейке в строке соответствует ходу (также 0 , 1 или 2 ).
    • Возможные ходы - это любые клетки на доске, в которых еще нет X или O .
    • Приемлемо любое возвращаемое значение, если в качестве входа используется клеммная колодка.
  • Функция result принимает плату и действие в качестве входных данных и должна возвращать новое состояние платы без изменения исходной платы.
    • Если действие не является допустимым действием для платы, ваша программа должна вызвать исключение.
    • Возвращенное состояние доски должно быть той доской, которая возникла бы в результате взятия исходной доски ввода и разрешения игроку, чей ход идет, сделать свой ход в ячейку, указанную действием ввода.
    • Важно отметить, что исходную плату следует оставить неизменной: поскольку Minimax в конечном итоге потребует учета множества различных состояний платы во время вычислений. Это означает, что простое обновление ячейки на плате само по себе не является правильной реализацией функции result .Скорее всего, вы захотите сначала сделать полную копию доски, прежде чем вносить какие-либо изменения.
  • Функция победителя должна принимать в качестве входных данных плату и возвращать победителя доски, если таковая имеется.
    • Если игрок X выиграл игру, ваша функция должна вернуть X . Если игрок O выиграл игру, ваша функция должна вернуть O .
    • Можно выиграть игру тремя ходами подряд по горизонтали, вертикали или диагонали.
    • Вы можете предположить, что будет не более одного победителя (то есть ни на одной доске никогда не будет двух игроков с тремя в ряд, так как это будет недопустимым состоянием доски).
    • Если победителя в игре нет (либо потому, что игра продолжается, либо потому, что она закончилась ничьей), функция должна вернуть Нет .
  • Функция терминала должна принимать плату в качестве входных данных и возвращать логическое значение, указывающее, окончена ли игра.
    • Если игра окончена, либо потому, что кто-то выиграл игру, либо потому, что все ячейки были заполнены, но никто не выиграл, функция должна вернуть True .
    • В противном случае функция должна вернуть False , если игра еще продолжается.
  • Утилита Функция должна принимать клемму платы в качестве входа и выводить утилиту платы.
    • Если X выиграл игру, полезность будет 1 .Если O выиграл игру, полезность будет -1 . Если игра закончилась вничью, полезность составляет 0 .
    • Вы можете предположить, что утилита будет вызываться только на плате , если терминал (плата) имеет значение True .
  • Функция minimax должна принимать в качестве входных данных доску и возвращать оптимальный ход для игрока по этой доске.
    • Возвращаемый ход должен быть оптимальным действием (i, j) , которое является одним из допустимых действий на доске.Если несколько ходов одинаково оптимальны, любой из этих ходов приемлем.
    • Если плата является клеммной колодкой, функция минимакс должна вернуть Нет .

Для всех функций, которые принимают плату в качестве входных данных, вы можете предположить, что это действительная плата (а именно, что это список, содержащий три строки, каждая с тремя значениями: X , O , или ПУСТО ). Вы не должны изменять предоставленные объявления функций (порядок или количество аргументов для каждой функции).

После того, как все функции будут реализованы правильно, вы сможете запустить python runner.py и играть против своего ИИ. А поскольку крестики-нолики - это ничья при оптимальной игре обеих сторон, вы никогда не сможете победить ИИ (хотя, если вы не играете оптимально, он может победить вас!)

Подсказки

  • Если вы хотите протестировать свои функции в другом файле Python, вы можете импортировать их со строками вроде из tictactoe import initial_state .
  • Вы можете добавить дополнительные вспомогательные функции в tictactoe.py при условии, что их имена не конфликтуют с именами функций или переменных, уже имеющихся в модуле.
  • Обрезка альфа-бета необязательна, но может повысить эффективность работы вашего ИИ!

Как отправить

У вас может не быть вашего кода в ветке ai50 / projects / 2020 / x / tictactoe , вложенной в какие-либо дополнительные подкаталоги (например, подкаталог с именем tictactoe или project0b ).То есть, если персонал попытается получить доступ к https://github.com/me50/USERNAME/blob/ai50/projects/2020/x/tictactoe/tictactoe.py , где USERNAME - ваше имя пользователя GitHub, именно там должен находиться ваш файл. Если ваш файл не находится в этом месте, когда сотрудники попытаются поставить оценку, ваша отправка будет отклонена.

  1. Перейдите по этой ссылке, войдите в свою учетную запись GitHub и нажмите Авторизовать cs50 . Затем установите флажок, указывающий, что вы хотите предоставить сотрудникам курса доступ к своим материалам, и нажмите Присоединиться к курсу .
  2. Установите Git и, при желании, установите submit50 .
  3. Если вы установили submit50 , выполните
      submit50 ai50 / projects / 2020 / x / tictactoe
      

    В противном случае, используя Git, переместите свою работу на https://github.com/me50/USERNAME.git , где USERNAME - ваше имя пользователя GitHub, в ветке с именем ai50 / projects / 2020 / x / tictactoe . .

  4. Отправьте эту форму.

Затем перейдите по адресу https: // cs50.me / cs50ai, чтобы увидеть ваш текущий прогресс!

.

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

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