Как создать крестики нолики в python
Перейти к содержимому

Как создать крестики нолики в python

  • автор:

Крестики-Нолики (Tic Tac Toe) с компьютером на Python. Мой первый шаг к Machine Learning. Часть 1

Всем привет. Я любитель Python и совсем недолго осваиваю язык всеми доступными способами. Моя цель — понять принципы машинного обучения и его взаимосвязь с нейросетью. Никакого опыта в IT не имел, тем не менее постараюсь излагать общепринятой терминологией, не судите строго. Моя основная профессия (оперирующий травматолог, кандидат наук) не менее сложная, далека от IT, но для упрощения работы в нее все больше внедряются AI и ML. Мною движет лишь интерес к современным технологиям, программированию.

В первой части покажу только основные этапы создания игры, где пользователь выбирает роль (Х или О), играя с компьютером. Поиск в сети Python аналогов дал только несколько вариантов игры с рандомным ответом компьютера. Мой целью в этой части стало самостоятельно научиться оценивать текущую позицию на поле «Крестики-Нолики» и подбирать оптимальный вариант следующего хода компьютера. К слову, уже перед окончанием статьи нашел готовую web-игру в google, где уже реализован такой подход. Тем интереснее было проверить себя и поделиться «изобретением колеса, но по-своему».

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

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

Содержание статьи:

  1. Зачем Крестикам‑Ноликам машинное обучение?
  2. Представление поля 3х3 в виде одномерного числового списка.
  3. Коротко о функциях.
  4. Карта лучших ходов и алгоритм для компьютера.
  5. Функция для Игрока.
  6. Недостаток алгоритма?
  7. Финальный код.
  8. Анонс второй части статьи

Зачем Крестикам‑Ноликам машинное обучение?

Ответ простой: шахматы и переводчики на основе ИИ. Эти популярные в жизни приложения по своей сути — сложная игра. Как мне казалось, все ходы как и переводы просчитать невероятно сложно. Однако движок на CHESS.com поражает, как и выходные результаты переведенного текста в DEEP-L. Оказывается, все профессиональные шахматные партии с XIX века внесли в базу ИИ (благо записывают их в виде шахматных формул). Затем разными алгоритмами организовали поиск оптимального хода. Выяснили, что оптимальный ход может устаревать, ведь шахматисты растут да и играет уже весь мир. А чем больше партий, тем достовернее их анализ (хоть и дольше). Придумали непрерывно пополнять шахматную базу в ИИ, снова обновлять лучшие варианты хода, расширяя критерии поиска. Так я решил, что надо подобрать какую-то простую известную игру, где машину можно обучить до идеала по этой схеме. Выбор пал Tic Tac Toe с полем 3х3. Но для начала мне нужно было еще ее создать с нуля. Обозначил себе этапы Machine Learning (тема второй части):

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

Представление поля 3х3 в виде одномерного числового списка

Я решил уйти от многомерных списков list, которые здесь напрашиваются для изображения поля. Игрока «Х» обозначил как «-1», «О» как «1». Незаполненное поле останется «0». Это числовой тип данных. Наша игра — это одномерный список с 9ю аргументами на позициях от 0 до 8, значит начало: TTT=[0, 0, 0, 0, 0, 0, 0, 0, 0] (рис. 1 и 2):

Рис. 1. Для удобства позиция в списке равна клетке на поле 3х3. Начало игры: TTT=[0, 0, 0, 0, 0, 0, 0, 0, 0]Рис. 2. Пример списка, описывающего текущее поле TTT=[-1, 0, 1, 0, -1, 0, 0, 0, 1].

Коротко о функциях

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

pygame: Выбрана простая графика и события. Библиотека осваивается за полчаса с гугл.

import pygame as pg, sys from pygame.locals import * import time, random width = 400 height = 400 white = (255, 255, 255) RED = (255, 0, 0) BLACK = (0, 0, 0) line_color = (10, 10, 10) # initializing pygame window pg.init() fps = 30 CLOCK = pg.time.Clock() screen = pg.display.set_mode((width, height + 100), 0, 32) pg.display.set_caption("Tic Tac Toe") # loading the images opening = pg.image.load('tic tac opening.png') x_img = pg.image.load('x.png') o_img = pg.image.load('o.png') # resizing images x_img = pg.transform.scale(x_img, (80, 80)) o_img = pg.transform.scale(o_img, (80, 80)) opening = pg.transform.scale(opening, (width, height + 100))

user_click(): Выбор мышкой нужного поля без изысков: координаты внутри сектора определяют его номер как на рис.1., что заложено в числовую переменную move.

def user_click(): # mouse click global move move = None # get coordinates of mouse click x, y = pg.mouse.get_pos() # get x,y of mouse click (cell 0-8) if (y < height / 3) and (x < width / 3): move = 0 elif (y < height / 3) and (x < width / 3 * 2): move = 1 elif (y < height / 3) and (x < width): move = 2 elif (y < height / 3 * 2) and (x < width / 3): move = 3 elif (y < height / 3 * 2) and (x < width / 3 * 2): move = 4 elif (y < height / 3 * 2) and (x < width): move = 5 elif (y < height) and (x < width / 3): move = 6 elif (y < height) and (x < width / 3 * 2): move = 7 elif (y < height) and (x < width): move = 8

Переменные: XO - это очередь Х (-1) или О (1) оказаться в списке TTT=[ ]. Перменные ХО и move по умолчанию None, глобальные, числовые, поскольку будут использованы в разных функциях. Говорящие за себя winner и draw - победитель и ничья.

XO = None # -1 is X-player, 1 is O-player move = None # numbers from 0 to 8 winner = None draw = False # TicTacToe 3x3 board TTT= [0,0,0,0,0,0,0,0,0] # game field is shared on 9 cells with determination of each one from left to right in upper,middle & lower row: # 0,1,2 - upper row # 3,4,5 - middle row # 6,7,8 - lower row # totaly = 3x3 field = 9 numbers (from 0 to 8 considering that list [TTT] starts with 0 position)

сheck_win(): Перебором комбинаций текущего поля ТТТ получаем три -1 или 1 в ряду (но и не ноль!), вертикали или диагонали. Программа рисует линию через победную комбинацию и присваивает переменной winner значение -1 (Х) или 1 (О).

Рис.3. Два примера диагональных побед и схематичный вид списка ТТТ при этом.

def check_win(): # check winner and drawing the appropriate lines global TTT, winner, draw # check for winning rows for row in range(0, 7, 3): # jump through 3 in TTT list if ((TTT[row] == TTT[row + 1] == TTT[row + 2]) and (TTT[row] != 0)): # this row won winner = TTT[row] pg.draw.line(screen, (250, 0, 0), (0, (row/3 + 1) * height / 3 - height / 6), \ (width, (row/3 + 1) * height / 3 - height / 6), 6) break # check for winning columns for col in range(0, 3, 1): # jump through 1 in TTT list if (TTT[col] == TTT[col + 3] == TTT[col + 6]) and (TTT[col] != 0): # this column won winner = TTT[col] # draw winning line pg.draw.line(screen, (250, 0, 0), ((col + 1) * width / 3 - width / 6, 0), \ ((col + 1) * width / 3 - width / 6, height), 6) break # check for diagonal winners if (TTT[0] == TTT[4] == TTT[8]) and (TTT[0] != 0): # game won diagonally left to right winner = TTT[0] pg.draw.line(screen, (250, 70, 70), (50, 50), (350, 350), 6) if (TTT[2] == TTT[4] == TTT[6]) and (TTT[2] != 0): # game won diagonally right to left winner = TTT[2] pg.draw.line(screen, (250, 70, 70), (350, 50), (50, 350), 6) if TTT.count(0) == 0 and winner is None: # all cells are filled in draw = True game_status()

Карта лучших ходов и алгоритм для компьютера

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

  1. Представим самый простой вариант (Рис.4.), когда на 7м ходу Х (ХО=-1) нужно поставить именно туда, где уже есть ХХ рядом согласно правилам (диагональ Лево-Право):

Рис.4. В желтую клетку просится Х для победы (список ТТТ[8]). Для этого в списке mov_map[8] появляется красный -1 (собственно желаемый будущий ход именно Х).

  1. Однако если, например, обороняется игрок О (ХО=1), а критическое победное поле Х уже создал (Рис. 5)? Тогда приоритет для хода О как раз та же желтая клетка (mov_map[8]=-1). Результат хода это предотвращение победы Х и ничья для игрока О:

Рис.5. Для игрока О только оборона в желтую клетку, поскольку mov_map[8]=-1 (иначе следующий ход Х сюда же приведет к его победе).

  1. Следующий вариант, если в одну клетку сходятся две победные комбинации игрока Х, при этом ход игрока О (ХО=1):

Рис.6. Редкое совпадение, когда значение mov_map[8]=-2.

  1. Комбинация, когда в одну клетку сходится и победа Х и победа О. Для того, чтобы избежать ноль после суммирования -1 и 1 лучше складывать модули значений, а затем умножать его на их знаки (проще говоря, если есть хоть один минус, то значение возвращется в список отрицательным во избежании путаницы в логике).
def check_moves(): # finding the best cell for the next computer's move global TTT, move mov_map = [0, 0, 0, 0, 0, 0, 0, 0, 0] # map of the moves before each computer's move in current situation move = None # check for winning rows # the sum of the moduli of the current value and the winning cell of the checked player, and then multiply them by the sign of the checked player # in the most cases: zero + 1 or -1 (current player), but if the cell has two or three winners simultaneously, the module of the value must be 2 or 3 (-2 or -3) for row in range(0, 7, 3): # jump through 1 in TTT list r=TTT[row] + TTT[row + 1] + TTT[row + 2] if abs(r) == 2: if TTT[row] == 0: mov = row mov_map[mov] = (abs(mov_map[mov])+abs(int((r) / 2)))*int((r) / 2) #the sum of winning's module both of players & multiple on the current player's sign elif TTT[row + 1] == 0: mov = row + 1 mov_map[mov] = (abs(mov_map[mov])+abs(int((r) / 2)))*int((r) / 2) elif TTT[row + 2] == 0: mov = row + 2 mov_map[mov] = (abs(mov_map[mov])+abs(int((r) / 2)))*int((r) / 2) # check for winning columns for col in range(0, 3, 1): # jump through 1 in TTT list c=TTT[col] + TTT[col + 3] + TTT[col + 6] if abs(c) == 2: if TTT[col] == 0: mov = col mov_map[mov] = (abs(mov_map[mov])+abs(int((c) / 2)))*int((c) / 2) elif TTT[col + 3] == 0: mov = col + 3 mov_map[mov] = (abs(mov_map[mov])+abs(int((c) / 2)))*int((c) / 2) elif TTT[col + 6] == 0: mov = col + 6 mov_map[mov] = (abs(mov_map[mov]) + abs(int((c) / 2))) * int((c) / 2) # check for diagonal winners: L>R d_Lr=TTT[0] + TTT[4] + TTT[8] if abs(d_Lr) == 2: if TTT[0] == 0: mov = 0 mov_map[mov] = (abs(mov_map[mov])+abs(int((d_Lr) / 2)))*int((d_Lr) / 2) elif TTT[4] == 0: mov = 4 mov_map[mov] = (abs(mov_map[mov])+abs(int((d_Lr) / 2)))*int((d_Lr) / 2) elif TTT[8] == 0: mov = 8 mov_map[mov] = (abs(mov_map[mov])+abs(int((d_Lr) / 2)))*int((d_Lr) / 2) # check for diagonal winners: R>L d_Rl=TTT[2] + TTT[4] + TTT[6] if abs(d_Rl) == 2: if TTT[2] == 0: mov = 2 mov_map[mov] = (abs(mov_map[mov])+abs(int((d_Rl) / 2)))*int((d_Rl) / 2) elif TTT[4] == 0: mov = 4 mov_map[mov] = (abs(mov_map[mov])+abs(int((d_Rl) / 2)))*int((d_Rl) / 2) elif TTT[6] == 0: mov = 6 mov_map[mov] = (abs(mov_map[mov])+abs(int((d_Rl) / 2)))*int((d_Rl) / 2)

Выбор хода после заполнения списка mov_map=[ ]:

Знак перед модулем значений в списке mov_map создает чувствительность для конкретного игрока. Разберем на примере, когда наступает ход Х (т.е. ХО=-1):

  1. если в списке mov_map хотя бы единожды встречается -1, то это победный выбор.
  2. если в списке mov_map -1 нет, а присутствует 1, то для Х (напомню, ХО=-1) это ход для спасения от поражения.
  3. если в списке mov_map присутствуют и -1 и 1, то выбирается модуль со своим знаком для победы (т.е. -1 для ХО=-1).
# if one winner in one cell if mov_map.count(XO) > 0 and mov_map.count(-1*XO) == 0: #current player must choose his own square if the opponent hasn't a winning cell move = mov_map.index(XO) if mov_map.count(-1*XO) > 0 and mov_map.count(XO) == 0: #current player must choose opponent's square if the there isn't his own winning cell move = mov_map.index(-1*XO) if mov_map.count(XO) > 0 and mov_map.count(-1*XO) > 0: # current player must choose his own square if the opponent has a winning cell as well move = mov_map.index(XO)
  1. Вне зависимости какой игрок (Х или О): любое значение с модулем 2 требует хода именно туда. Там сходятся или две победы одного игрока, или победа у каждого.
# if two winners or double one are in one cell - the always preference goes to current player if mov_map.count(2) > 0: move = mov_map.index(2) if mov_map.count(-2) > 0: move = mov_map.index(-2)

Функция для Игрока

Раздельный способ запуска игры для Х- или О- пользователя показался более оптимальным на отладке во избежании путаницы и усложнения логики.

X_player(). Запускает главный цикл while True и обработку события выхода из игры (стандартный цикл for event. в pygame).

def X_player(): # X-player global TTT, XO, move, winner, draw while (True): # run the game loop forever for event in pg.event.get(): if event.type == QUIT: pg.quit() sys.exit()

Один ход на каждого игрока - это две ветки:

  1. Игрок Х - пользователь - (ХО==-1) выбирает мышкой желаемую клетку на поле функцией user_click(), которая должна возвратить значение от 0 до 8 переменной move (согласно схеме на рис.1). Если move=None, значит ожидается повторный клик. Удобно обходиться без обработки ошибок если пользователь промахивается мимо поля, просто разместив это условие в цикле на первом месте с повтором (continue).
  2. Если все же пользователь указал пустую клетку TTT[move] == 0, то запускается функция прорисовки фигуры на поле DrawXO() (см. ниже).
 if XO == -1: # X's move if event.type == MOUSEBUTTONDOWN: user_click() # click mouse button for Х's move if move == None: continue else: if (TTT[move] == 0): DrawXO()
  1. Игрок О - компьютер - (ХО==1):
    • Проверка наличия оптимальных ходов: check_moves(). Если их нет, то запускается локальный цикл while True:
    • Попытка поставить "О" в центр TTT[4] == 0 ("защита от дурака", т.к. это ослабит большое преимущество у Х после первого хода).
    • При исключении - случайный ход "О", но только в пустое место: random.
    • Прорисовка, переход хода и проверка в DrawXO() (см. ниже).
 if XO == 1 and draw is False and winner is None: # O's move check_moves() # check for XX, X_X, OO, O_O if move is None: while True: if TTT[4] == 0: # protection from the fool (when a rival makes typical triangle of "X") move = 4 break else: # a move for good luck, gives a chance to play fair without algorithm move = random.randint(0, 8) if TTT[move] == 0: break DrawXO()
  1. В случае чьей-то победы или ничьи - перезагрузка reset_game().
  2. В главном цикле обновляется картинка на экране pg.display.update().
 if (winner or draw): reset_game() pg.display.update() CLOCK.tick(fps)

DrawXO(). Запускается только при имеющемся готовом значении хода move.

  • Сначала вносит в список новый ход: TTT[move] = XO.
  • Затем прорисовывает Х (или О) в своем поле.
  • После этого XO = -1*XO дает тригер для перехода хода (от Х к О или наоборот), меняя свой знак на обратный.
  • В финале проверяет все текущее поле ТТТ на победу check_win().
def DrawXO(): # drawing of X or O, and after a sign will be reversed (XO => - XO) for player changing global TTT, XO, move TTT[move] = XO if move == 0: posx = 30 posy = 30 if move == 1: posx = width / 3 + 30 posy = 30 if move == 2: posx = width / 3 * 2 + 30 posy = 30 if move == 3: posx = 30 posy = height / 3 + 30 if move == 4: posx = width / 3 + 30 posy = height / 3 + 30 if move == 5: posx = width / 3 * 2 + 30 posy = height / 3 + 30 if move == 6: posx = 30 posy = height / 3 * 2 + 30 if move == 7: posx = width / 3 + 30 posy = height / 3 * 2 + 30 if move == 8: posx = width / 3 * 2 + 30 posy = height / 3 * 2 + 30 if XO == -1: screen.blit(x_img, (posx, posy)) else: screen.blit(o_img, (posx, posy)) XO = -1*XO pg.display.update() check_win()

Один полноценный главный цикл X_player() это:

  1. ход Х (пользователь), прорисовка и смена игрока, проверка на победу.
  2. алгоритм поиска хода для О (компьютер), прорисовка и смена игрока, проверка на победу.

O_player(). Устроена аналогично, но зеркально наоборот.

Недостаток алгоритма?

С точки зрения пользователя - недостатки алгоритма компьютерного хода - это увеличенные шансы для победы человека. На поле 3х3 важны первые три хода, когда О-игрок мешает Х сделать вилку, т.е. одновременно появляются две клетки для безоговорочной победы Х (Рис.7).

Рис.7. Игрок Х сделал вилку. Ход О-игрока в красную или желтую клетку приведет к победе Х.

Таким образом, если проработать все варианты дебютов "от вилки" (их три), то алгоритм станет мощнее. Однако это окочательно "засушит" игру до постоянной ничьи. Например, у детей пропадет всякий азарт и желание сыграть (проверил на своих). Поэтому решено оставить только "защиту от дурака" в виде постановки "О" в центр, что уменьшило шансы для победы "Х", но не исключило их. Либо можно отказаться даже от этого ради азарта.

Финальный код

Начальное меню и выбор пользователем роли в статье не рассматривалось, там все предельно просто и понятно.

Проект финального кода и три картинки для скачивания находятся в архивном файле здесь.

Анонс второй части статьи

  1. Запуск игр компьютера против самого себя.
  2. Функции для записи всех игр пошагово в CSV-файл.
  3. Анализ и выбор нужного хода из базы данных: какой способ оптимален?
  4. Результаты: это ли машинное обучение?

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

Статьи

Автор Admin На чтение 5 мин Просмотров 8.1к. Опубликовано 09.11.2022

Введение

В статье напишем игру «Крестики-нолики» на Python.

Крестики-нолики — логическая игра между двумя соперниками на квадратном поле 3×3 клетки, или бо́льшего размера. Один из игроков играет за «крестики», а второй за «нолики».

Рисуем игровое поле

Начнём с того, что нарисуем само игровое поле для игры.

Для начала сгенерируем список с числами от одного, до 9:

board = list(range(1, 10))

Создадим функцию draw_board(), аргументом которой будет board:

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

В функции выводим первую строку состоящую из 13 символов «тире», после чего, в цикле прорисовываем остальные края поля.

При вызове функции будет следующий вывод:

Принимаем ввод пользователя

Теперь нам нужно создать функцию для приёма ввода.

Создадим функцию take_input() с аргументом player_token:

def take_input(player_token): valid = False while not valid: player_answer = input("Куда поставим " + player_token + "? ") try: player_answer = int(player_answer) except ValueError: print("Некорректный ввод. Вы уверены, что ввели число?") continue if 1 

Внутри функции сначала задаётся переменная valid, которая равняется False, после чего идёт цикл while, который не закончится, пока valid не примет значение True. В цикле производится ввод пользователем определённой клетки, в которую будет ставиться крестик, либо нолик. Если же пользователь ввёл, а какой-либо другой символ, выведется ошибка.

Далее в условии проверяется, занята ли введённая клетка. Если клетка занята, то выведется соответствующая ошибка, если же введено число не в диапазоне от 1, до 10 — будет так же выведено соответствующее сообщение.

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

Создадим функцию check_win(), в которой будем проверять, выиграл ли игрок. Аргументом функции будет board:

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

Внутри функции создаётся кортеж win_coord, в котором хранятся победные комбинации. В цикле производится проверка на победу игрока, если он побеждает, то выводится сообщение о победе, если же нет — возвращается False, и игра продолжается.

Создание главной функции

Теперь создадим функцию main() с аргументом board:

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 tmp = check_win(board) if tmp: print(tmp, "выиграл!") win = True break if counter == 9: print("Ничья!") break draw_board(board)

Внутри функции, после обозначения переменных, создаётся цикл, который закончится после победы одного из игроков, или ничьей. Внутри цикла проводится проверка, какой игрок сходил, после чего вызывается функция take_input() с соответствующим символом игрока. Далее идёт проверка, какой игрок выиграл, или вышла ничья.

Итоговый код игры «Крестики-нолики» на Python

board = list(range(1, 10)) def draw_board(board): print("-" * 13) for i in range(3): print("|", board[0 + i * 3], "|", board[1 + i * 3], "|", board[2 + i * 3], "|") print("-" * 13) def take_input(player_token): valid = False while not valid: player_answer = input("Куда поставим " + player_token + "? ") try: player_answer = int(player_answer) except ValueError: print("Некорректный ввод. Вы уверены, что ввели число?") continue if 1  

Заключение

В статье мы с Вами написали игру «Крестики-нолики» на Python! Надеюсь Вам понравилась статья, желаю удачи и успехов! ��

Создайте программу, которая реализует игру «Крестики-нолики» через ООП

Author24 — интернет-сервис помощи студентам

Создайте программу, которая реализует игру «Крестики-нолики».

Для этого напишите:

1. Класс, который будет описывать поле игры.

# Класс поля, который создаёт у себя экземпляры клетки.

# Пусть класс хранит информацию о состоянии поля (это может быть список из девяти элементов).

# Помимо этого, класс должен содержать методы:

# «Изменить состояние клетки». Метод получает номер клетки и, если клетка не занята, меняет её состояние. Если состояние удалось изменить, метод возвращает True, иначе возвращается False.

# «Проверить окончание игры». Метод не получает входящих данных, но возвращает True/False. True — если один из игроков победил, False — если победителя нет.

2. Класс, который будет описывать одну клетку поля:

# Клетка, у которой есть значения:

# занята она или нет;

# символ, который клетка хранит (пустая, крестик, нолик).

3. Класс, который описывает поведение игрока:

# У игрока может быть:

# Класс должен содержать метод:

# «Сделать ход». Метод ничего не принимает и возвращает ход игрока (номер клетки). Введённый номер нужно обязательно проверить.

4. Класс, который управляет ходом игры:

# класс «Игры» содержит атрибуты:

# Метод запуска одного хода игры. Получает одного из игроков, запрашивает у игрока номер клетки, изменяет поле, проверяет, выиграл ли игрок. Если игрок победил, возвращает True, иначе False.

# Метод запуска одной игры. Очищает поле, запускает цикл с игрой, который завершается победой одного из игроков или ничьей. Если игра завершена, метод возвращает True, иначе False.

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

Здесь вы можете заказать любую студенческую или школьную работу.

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

ООП. Написать игру крестики-нолики.
Есть вот такое вот задание Написать игру крестики-нолики, используя как можно больше ООП. Общие.

Напишите программу, которая имитирует игру "Крестики-нолики".
Здравствуйте ,у нас в универе ведут дисциплину ФЛП ,дали миллион и одну задачку,знать не знаю как.

Составить программу, моделирующую игру «крестики-нолики»
Для указания позиции крестика или нолика играющего использовать номер строки и номер столбца, в.

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

Написать игру «Крестики-нолики»
4. Написать игру «Крестики-нолики».

Регистрация: 25.09.2022
Сообщений: 130

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
import os class Cell: def __init__(self, num): self.num = num self.symbol = ' ' def __str__(self): return self.symbol class Board: def __init__(self): self.cells = [] for i in range(9): self.cells.append(Cell(i+1)) def display(self): for i in range(3): print('-------------') out = '| ' for j in range(3): out += str(self.cells[i*3+j]) + ' | ' print(out) print('-------------') def update(self, cell_num, symbol): if self.cells[cell_num-1].symbol == ' ': self.cells[cell_num-1].symbol = symbol return True else: return False def is_game_over(self): for i in range(3): if self.cells[i*3].symbol == self.cells[i*3+1].symbol == self.cells[i*3+2].symbol and self.cells[i*3].symbol != ' ': return True for i in range(3): if self.cells[i].symbol == self.cells[i+3].symbol == self.cells[i+6].symbol and self.cells[i].symbol != ' ': return True if self.cells[0].symbol == self.cells[4].symbol == self.cells[8].symbol and self.cells[0].symbol != ' ': return True if self.cells[2].symbol == self.cells[4].symbol == self.cells[6].symbol and self.cells[2].symbol != ' ': return True for cell in self.cells: if cell.symbol == ' ': return False return True class Player: def __init__(self, name, symbol): self.name = name self.symbol = symbol self.score = 0 def get_move(self): try: cell_num = int(input(self.name + ', enter cell number: ')) return cell_num except ValueError: print('Please enter a number.') return self.get_move() class Game: def __init__(self, player1, player2): self.player1 = player1 self.player2 = player2 self.board = Board() self.current_player = player1 def play_turn(self): os.system('cls' if os.name == 'nt' else 'clear') print(self.current_player.name + '\'s turn:\n') self.board.display() cell_num = self.current_player.get_move() while not self.board.update(cell_num, self.current_player.symbol): print('Cell is already occupied. Try again.') cell_num = self.current_player.get_move() if self.board.is_game_over(): os.system('cls' if os.name == 'nt' else 'clear') print(self.current_player.name + ' wins!\n') self.board.display() self.current_player.score += 1 return True if self.current_player == self.player1: self.current_player = self.player2 else: self.current_player = self.player1 return False def play_game(self): os.system('cls' if os.name == 'nt' else 'clear') print('New game started!') self.board = Board() self.current_player = self.player1 while not self.board.is_game_over(): if self.play_turn(): break print('Score:') print(self.player1.name + ': ' + str(self.player1.score)) print(self.player2.name + ': ' + str(self.player2.score)) while True: name1 = input('Enter name for Player 1 (X): ') name2 = input('Enter name for Player 2 (O): ') player1 = Player(name1, 'X') player2 = Player(name2, 'O') game = Game(player1, player2) game.play_game() again = input('Do you want to play again? (Y/N): ') if again.lower() != 'y': break print('Thanks for playing!')

Регистрация: 11.04.2023
Сообщений: 2
Спасибо большое.

Эксперт С++

13397 / 10674 / 6379
Регистрация: 18.12.2011
Сообщений: 28,506
Модифицировал программу Informatikc под поле произвольного размера:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
import os class Cell: def __init__(self, num): self.num = num self.symbol = ' ' def __str__(self): return self.symbol class Board: def __init__(self,n): self.cells = [] self.n = n self.win = 5 for i in range(n * n): self.cells.append(Cell(i + 1)) def display(self): n = self.n print(" ",end='') for i in range(n): print(chr(ord('A') + i),end=' ') print("") for i in range(n): for j in range(n+1): print('----',end='') print('-') print("".format(i + 1),end=" ") out = '| ' for j in range(n): c = self.cells[i * n + j] out += str(c) + ' | ' print(out) for j in range(n+1): print('----',end='') print('-') def update(self, cell_num, symbol): if self.cells[cell_num - 1].symbol == ' ': self.cells[cell_num - 1].symbol = symbol return True return False def is_game_over(self): n = self.n for i in range(n): for j in range(n - self.win): pos = i * n + j if self.cells[pos].symbol != ' ': wk = 0 for k in range(self.win-1): wk += self.cells[pos + k].symbol == self.cells[pos + k + 1].symbol if wk == self.win-1: return True for j in range(n): for i in range(n - self.win): pos = i * n + j if self.cells[pos].symbol != ' ': wk = 0 for k in range(self.win-1): wk += self.cells[pos + k * n].symbol == self.cells[pos + (k + 1) * n].symbol if wk == self.win-1: return True for i in range(n - self.win): for j in range(n - self.win): pos = i * n + j if self.cells[pos].symbol != ' ': wk = 0 for k in range(self.win-1): wk += self.cells[pos + k + k * n].symbol == self.cells[pos + (k + 1) * n + k + 1].symbol if wk == self.win-1: return True for i in range(n - self.win): for j in range(self.win-1,n): pos = i * n + j if self.cells[pos].symbol != ' ': wk = 0 for k in range(self.win-1): wk += self.cells[pos - k + k * n].symbol == self.cells[pos + (k + 1) * n - k - 1].symbol if wk == self.win-1: return True for cell in self.cells: if cell.symbol == ' ': return False return True class Player: def __init__(self, name, symbol): self.name = name self.symbol = symbol self.score = 0 def get_move(self): cell_num=input(self.name + ', enter cell number: ') return cell_num class Game: def __init__(self, player1, player2): self.player1 = player1 self.player2 = player2 self.num = int(input('Enter boad with: ')) self.board = Board(self.num) self.current_player = player1 def play_turn(self): os.system('cls' if os.name == 'nt' else 'clear') print(self.current_player.name + '\'s turn:\n') self.board.display() t = self.current_player.get_move() t = t.upper() nn=int(t[1:])-1 cell_num = 1+(ord(t[0])-ord('A')) + nn * self.num while not self.board.update(cell_num, self.current_player.symbol): print('Cell is already occupied. Try again.') t = self.current_player.get_move() t = t.upper() nn=int(t[1:])-1 cell_num = 1+(ord(t[0])-ord('A')) + nn * self.num if self.board.is_game_over(): os.system('cls' if os.name == 'nt' else 'clear') print(self.current_player.name + ' wins!\n') self.board.display() self.current_player.score += 1 return True if self.current_player == self.player1: self.current_player = self.player2 else: self.current_player = self.player1 return False def play_game(self): os.system('cls' if os.name == 'nt' else 'clear') print('New game started!') self.board = Board(self.num) self.current_player = self.player1 while not self.board.is_game_over(): if self.play_turn(): break print('Score:') print(self.player1.name + ': ' + str(self.player1.score)) print(self.player2.name + ': ' + str(self.player2.score)) name1 = input('Enter name for Player1 (X): ') name2 = input('Enter name for Player2 (O): ') player1 = Player(name1, 'x') player2 = Player(name2, 'o') game = Game(player1, player2) while True: game.play_game() again = input('Do you want to play again? (Y/N): ') if again.lower() != 'y': break print('Thanks for playing!')

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

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

Используя Python, мы создадим CLI-игру в крестики-нолики. Давайте используем Python для создания простой игры Tic Tac Toe. Это поможет вам создать игровую логику и научиться организовывать код.

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

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

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

С консоли будет введено число от 1 до 9. В сетке, где каждому числу соответствует другое число (вверху слева — 1, внизу справа — 9).

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

Какая функция завершается, вероятно, указано в каждом заголовке этого текстового урока.

функции

Проектирование доски

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

Поскольку пользователь может вводить только числа от 1 до 9, а не от 0 до 8, у нас есть 10 пустых значений, а не 9. Итак, чтобы упростить ситуацию, мы присвоим первой записи нашего списка значение «пустой текст».

Таким образом, мы можем использовать 1–9 вместо 0–8 для индексации записей в нашем списке.

Проектирование доски

вставить письмо ()

Letter и Pos будут двумя входами для этой функции. Он только поместит запрошенное письмо в запрошенное место.

Вставить письмо

пространствосвободно (поз.)

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

Пространство бесплатно

печатная доска (доска)

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

Распечатать доску

Победитель()

На основе существующей доски эта функция сообщит нам, выиграла ли поставленная буква. Имеет два параметра: le и board (буква).

Это должно быть «Х» или «О». Просто посмотрите, присутствует ли указанная буква на каждой потенциально выигрышной линии на доске.

победитель

Основной ()

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

Главная

isBoardFull()

При задании на борту в качестве входных данных этот метод просто вернет True, если доска заполнена, и False в противном случае.

Полная доска

игрокПереместить()

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

Движение игрока

CompMove () — ИИ

Теперь начнется ИИ. Эта функция будет контролировать движение компьютеров. Он оценит доску и выберет лучший ход.

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

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

Компьютерный ИИ

выбратьслучайный()

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

Случайный ход

Начало игры

Теперь игру можно начинать, когда мы выполнили все наши задачи. Все, что нам нужно сделать, это вызвать main, если мы просто хотим запустить игру один раз.

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

Начало игр

Полный код

Вот весь код для нашей игры, который теперь завершен.

Tic Tac Toe

Результат

Выход 1

Заключение

Игра, которую вы сделали полностью с нуля. Это не та игра, в которую мы играем регулярно. Но это помогает в написании логики и поддержании четкой структуры кода.

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

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