diff --git a/lab_6/lab6.ipynb b/lab_6/lab6.ipynb
new file mode 100644
index 0000000..7253d6d
--- /dev/null
+++ b/lab_6/lab6.ipynb
@@ -0,0 +1,1192 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Игра «Крестики-нолики».\n",
+ "\n",
+ "## Исходный проект: [GitHub](https://github.com/nczempin/gym-tic-tac-toe).\n",
+ "\n",
+ "### Описание проекта:\n",
+ "\n",
+ "Проект представляет собой реализацию среды для игры в крестики-нолики с использованием библиотеки Gymnasium и модель обучения, в которой два случайно действующих игрока соревнуются в течение множества эпизодов.\n",
+ "\n",
+ "**Проект состоит из следующих компонентов:**\n",
+ "1. **Среда TicTacToeEnv:** \n",
+ " Реализует правила игры в крестики-нолики, управление ходами, проверку победных условий и завершения игры. Обеспечивает визуализацию доски (render) и генерацию доступных ходов.\n",
+ "2. **Игроки:** \n",
+ " Два случайных игрока, каждый из которых выбирает ход из доступных с помощью функции random_move.\n",
+ "3. **Цикл обучения:** \n",
+ " Выполняется симуляция игр (эпизодов) между игроками. Учитываются награды, проверяются завершения игр и ведётся статистика (например, средняя награда).\n",
+ "4. **Параметры обучения:** \n",
+ " Задаются переменные (alpha, beta) для возможной адаптации стратегий, хотя в текущей версии они не используются.\n",
+ "5. **Сбор данных:** \n",
+ " Коллекционируются награды и результаты эпизодов для анализа и оценки эффективности случайных стратегий. \n",
+ "\n",
+ "---\n",
+ "\n",
+ "### Бизнес-цель:\n",
+ "Разработка платформы для исследования и оптимизации стратегий игры, которая может быть использована в обучении, автоматизации решений, создании игровых приложений или демонстрации алгоритмов искусственного интеллекта.\n",
+ "\n",
+ "---\n",
+ "\n",
+ "### Технические цели:\n",
+ "1. Разработать гибкую и расширяемую среду для симуляции игры в крестики-нолики, которая будет легко интегрироваться с различными алгоритмами искусственного интеллекта (AI), включая классические методы обучения с подкреплением, такие как Q-learning.\n",
+ "2. Реализовать и оптимизировать алгоритмы обучения с подкреплением для создания агентов, которые могут адаптироваться и улучшать свои стратегии с течением времени. Это включает внедрение таких методов, как Q-learning, для оптимизации принятия решений агентами.\n",
+ "3. Оценить эффективность различных стратегий, путем обучения агентов с различными параметрами, такими как коэффициенты обучения и коэффициенты дисконтирования, и выявить наилучшие подходы для разных игровых ситуаций.\n",
+ "4. Разработать механизмы автоматизированного тестирования и оптимизации стратегий игры. Это включает реализацию автоматической генерации и оценки множества стратегий с использованием методов оптимизации и обучения с подкреплением.\n",
+ "5. Встроить функционал для сравнения различных стратегий по меткам, таким как количество побед, ничьих и полученные награды, что будет полезно для исследований и разработки более эффективных игровых стратегий.\n",
+ "6. Реализовать возможность визуализации и подробной статистики для отслеживания эффективности выбранных стратегий и анализа поведения алгоритмов.\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Реализация среды:\n",
+ "\n",
+ "**Среда (environment)** – среда, в которой объект выполняет действия для решения задачи.\n",
+ "\n",
+ "Класс TicTacToeEnv реализует среду для игры в крестики-нолики с использованием библиотеки gymnasium. Он предоставляет интерфейс для двух игроков, которые по очереди делают ходы, стремясь выстроить три символа в ряд (по горизонтали, вертикали или диагонали).\n",
+ "\n",
+ "Среда определяет пространство действий (action_space), состоящее из 9 дискретных значений, соответствующих номерам клеток на доске, и пространство наблюдений (observation_space), описывающее текущее состояние игры. Начальное состояние доски задаётся с помощью метода reset, где доска очищается, а первым ходит игрок 1 (крестики).\n",
+ "\n",
+ "Основной метод step обрабатывает действия игроков, проверяет их легальность (например, нельзя занять уже занятую клетку или сделать ход не в свою очередь), обновляет состояние доски и проверяет условия завершения игры: победу одного из игроков или ничью. Если ход незаконный, игра завершается, а противнику начисляется награда. Легальный ход обновляет состояние доски, переключает текущего игрока и проверяет наличие трёх одинаковых символов в одной линии.\n",
+ "\n",
+ "Метод render визуализирует состояние игры, показывая текущего игрока и доску в текстовом формате. Также реализован метод move_generator, который возвращает список возможных ходов для текущего игрока, исключая уже занятые клетки."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 62,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import gymnasium as gym\n",
+ "from gymnasium import spaces\n",
+ "\n",
+ "\n",
+ "class TicTacToeEnv(gym.Env):\n",
+ " metadata = {'render.modes': ['human']}\n",
+ " \n",
+ " symbols = ['O', ' ', 'X'];\n",
+ "\n",
+ " def __init__(self) -> None:\n",
+ " super().__init__()\n",
+ " self.action_space = spaces.Discrete(9)\n",
+ " self.observation_space = spaces.Discrete(9*3*2) # flattened\n",
+ " self.reset()\n",
+ " \n",
+ " def step(self, action):\n",
+ " done = False\n",
+ " reward = 0\n",
+ "\n",
+ " p, square = action\n",
+ " \n",
+ " # check move legality\n",
+ " board = self.state['board']\n",
+ " proposed = board[square]\n",
+ " om = self.state['on_move']\n",
+ " \n",
+ " if proposed != 0: # wrong player, not empty\n",
+ " print(\"illegal move \", action, \". (square occupied): \", square)\n",
+ " done = True\n",
+ " reward = -1 * om # player who did NOT make the illegal mov\n",
+ " if p != om: # wrong player, not empty\n",
+ " print(\"illegal move \", action, \" not on move: \", p)\n",
+ " done = True\n",
+ " reward = -1 * om # player who did NOT make the illegal move\n",
+ " else:\n",
+ " board[square] = p\n",
+ " self.state['on_move'] = -p\n",
+ "\n",
+ " # check game over\n",
+ " for i in range(3):\n",
+ " # horizontals and verticals\n",
+ " if ((board[i * 3] == p and board[i * 3 + 1] == p and board[i * 3 + 2] == p)\n",
+ " or (board[i + 0] == p and board[i + 3] == p and board[i + 6] == p)):\n",
+ " reward = p\n",
+ " done = True\n",
+ " break\n",
+ " # diagonals\n",
+ " if ((board[0] == p and board[4] == p and board[8] == p)\n",
+ " or (board[2] == p and board[4] == p and board[6] == p)):\n",
+ " reward = p\n",
+ " done = True\n",
+ " \n",
+ " return self.state, reward, done, {}\n",
+ " \n",
+ " def reset(self):\n",
+ " self.state = {}\n",
+ " self.state['board'] = [0, 0, 0, 0, 0, 0, 0, 0, 0]\n",
+ " self.state['on_move'] = 1\n",
+ " return self.state\n",
+ " \n",
+ " def render(self, close=False):\n",
+ " if close:\n",
+ " return\n",
+ " print(\"on move: \" , self.symbols[self.state['on_move']+1])\n",
+ " for i in range(9):\n",
+ " print(self.symbols[self.state['board'][i]+1], end=\" \")\n",
+ " if i % 3 == 2:\n",
+ " print()\n",
+ " print()\n",
+ " \n",
+ " def move_generator(self):\n",
+ " moves = []\n",
+ " for i in range(9):\n",
+ " if self.state['board'][i] == 0:\n",
+ " p = self.state['on_move']\n",
+ " m = [p, i]\n",
+ " moves.append(m)\n",
+ " return moves"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Реализация основного цикла обучения:\n",
+ "\n",
+ "Основной цикл обучения выполняет симуляцию игры в крестики-нолики между двумя случайно действующими игроками в течение заданного числа эпизодов (num_episodes). Каждая игра моделируется как последовательность ходов, пока один из игроков не победит, не будет сделан незаконный ход или не наступит ничья.\n",
+ "\n",
+ "**Описание логики цикла:**\n",
+ "1. **Инициализация:** \n",
+ " Перед началом эпизодов задаются параметры обучения: коэффициенты alpha и beta, общее количество эпизодов (num_episodes), список для накопления наград (collected_rewards), и переменная oom для указания начального игрока.\n",
+ "2. **Начало эпизода:**\n",
+ " - Сбрасывается среда с помощью env.reset(), что очищает доску и задаёт начального игрока.\n",
+ " - Счётчик наград total_reward обнуляется, а флаги (done) и очередность хода (om) обновляются.\n",
+ "3. **Цикл ходов в одном эпизоде:**\n",
+ " - Генерируется список доступных ходов с помощью move_generator. Если нет доступных ходов, игра заканчивается.\n",
+ " - Если доступен только один возможный ход, он выполняется. В противном случае выбирается случайный ход с помощью функции random_move.\n",
+ " - Выполняется выбранный ход методом step, который возвращает новое состояние среды, награду, индикатор завершения игры и дополнительные данные.\n",
+ " - Суммируется полученная награда, обновляется текущее состояние, а очередь хода передаётся другому игроку (om = -om).\n",
+ " - Если игра завершена (done = True), цикл прерывается.\n",
+ "4. **Сбор статистики:**\n",
+ " - Награда за эпизод сохраняется в список collected_rewards.\n",
+ " - Каждые 50 эпизодов выводится информация о прогрессе: рендерится текущая доска, печатается общий выигрыш за эпизод и средняя награда за все предыдущие эпизоды.\n",
+ "5. **Повторение:** \n",
+ " Цикл продолжается до завершения заданного количества эпизодов. После каждого эпизода накапливаются данные для анализа и настройки параметров, если необходимо."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 63,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "on move: O\n",
+ " \n",
+ "X \n",
+ " \n",
+ "\n",
+ "on move: X\n",
+ " \n",
+ "X \n",
+ " O \n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ "X \n",
+ "X O \n",
+ "\n",
+ "on move: X\n",
+ " \n",
+ "X O \n",
+ "X O \n",
+ "\n",
+ "on move: O\n",
+ " X \n",
+ "X O \n",
+ "X O \n",
+ "\n",
+ "on move: X\n",
+ " X \n",
+ "X O O \n",
+ "X O \n",
+ "\n",
+ "on move: O\n",
+ "X X \n",
+ "X O O \n",
+ "X O \n",
+ "\n",
+ "Episode 50, Total Reward: 1\n",
+ "Average Reward: 0.32\n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ " \n",
+ "X \n",
+ "\n",
+ "on move: X\n",
+ "O \n",
+ " \n",
+ "X \n",
+ "\n",
+ "on move: O\n",
+ "O \n",
+ " \n",
+ "X X \n",
+ "\n",
+ "on move: X\n",
+ "O \n",
+ " O \n",
+ "X X \n",
+ "\n",
+ "on move: O\n",
+ "O \n",
+ "X O \n",
+ "X X \n",
+ "\n",
+ "on move: X\n",
+ "O O \n",
+ "X O \n",
+ "X X \n",
+ "\n",
+ "on move: O\n",
+ "O O \n",
+ "X X O \n",
+ "X X \n",
+ "\n",
+ "on move: X\n",
+ "O O O \n",
+ "X X O \n",
+ "X X \n",
+ "\n",
+ "Episode 100, Total Reward: -1\n",
+ "Average Reward: 0.35\n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ "X \n",
+ " \n",
+ "\n",
+ "on move: X\n",
+ " \n",
+ "X \n",
+ " O \n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ "X X \n",
+ " O \n",
+ "\n",
+ "on move: X\n",
+ " \n",
+ "X X O \n",
+ " O \n",
+ "\n",
+ "on move: O\n",
+ "X \n",
+ "X X O \n",
+ " O \n",
+ "\n",
+ "on move: X\n",
+ "X \n",
+ "X X O \n",
+ " O O \n",
+ "\n",
+ "on move: O\n",
+ "X \n",
+ "X X O \n",
+ "X O O \n",
+ "\n",
+ "Episode 150, Total Reward: 1\n",
+ "Average Reward: 0.30666666666666664\n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ " X \n",
+ " \n",
+ "\n",
+ "on move: X\n",
+ "O \n",
+ " X \n",
+ " \n",
+ "\n",
+ "on move: O\n",
+ "O \n",
+ " X \n",
+ " X \n",
+ "\n",
+ "on move: X\n",
+ "O O \n",
+ " X \n",
+ " X \n",
+ "\n",
+ "on move: O\n",
+ "O O \n",
+ "X X \n",
+ " X \n",
+ "\n",
+ "on move: X\n",
+ "O O \n",
+ "X X \n",
+ "O X \n",
+ "\n",
+ "on move: O\n",
+ "O O \n",
+ "X X X \n",
+ "O X \n",
+ "\n",
+ "Episode 200, Total Reward: 1\n",
+ "Average Reward: 0.295\n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ " X \n",
+ " \n",
+ "\n",
+ "on move: X\n",
+ " \n",
+ " X \n",
+ " O \n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ " X \n",
+ "X O \n",
+ "\n",
+ "on move: X\n",
+ " \n",
+ " X \n",
+ "X O O \n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ " X X \n",
+ "X O O \n",
+ "\n",
+ "on move: X\n",
+ " O \n",
+ " X X \n",
+ "X O O \n",
+ "\n",
+ "on move: O\n",
+ " O \n",
+ "X X X \n",
+ "X O O \n",
+ "\n",
+ "Episode 250, Total Reward: 1\n",
+ "Average Reward: 0.276\n",
+ "\n",
+ "on move: O\n",
+ " \n",
+ " \n",
+ "X \n",
+ "\n",
+ "on move: X\n",
+ " O \n",
+ " \n",
+ "X \n",
+ "\n",
+ "on move: O\n",
+ " O \n",
+ "X \n",
+ "X \n",
+ "\n",
+ "on move: X\n",
+ " O \n",
+ "X \n",
+ "X O \n",
+ "\n",
+ "on move: O\n",
+ " O \n",
+ "X X \n",
+ "X O \n",
+ "\n",
+ "on move: X\n",
+ " O O \n",
+ "X X \n",
+ "X O \n",
+ "\n",
+ "on move: O\n",
+ " O O \n",
+ "X X \n",
+ "X O X \n",
+ "\n",
+ "on move: X\n",
+ " O O \n",
+ "X O X \n",
+ "X O X \n",
+ "\n",
+ "Episode 300, Total Reward: -1\n",
+ "Average Reward: 0.2833333333333333\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "import random\n",
+ "\n",
+ "\n",
+ "def random_move(moves):\n",
+ " m = random.choice(moves)\n",
+ " return m\n",
+ "\n",
+ "\n",
+ "env = TicTacToeEnv()\n",
+ "\n",
+ "alpha = 0.01\n",
+ "beta = 0.01\n",
+ "\n",
+ "num_episodes = 300\n",
+ "\n",
+ "collected_rewards = []\n",
+ "oom = 1\n",
+ "\n",
+ "for i in range(num_episodes):\n",
+ " state = env.reset()\n",
+ " \n",
+ " total_reward = 0\n",
+ " \n",
+ " done = False\n",
+ " om = oom;\n",
+ "\n",
+ " for j in range(9):\n",
+ " moves = env.move_generator()\n",
+ " if not moves:\n",
+ " break\n",
+ " \n",
+ " if len(moves) == 1:\n",
+ " # only a single possible move\n",
+ " move = moves[0]\n",
+ " else:\n",
+ " move = random_move(moves)\n",
+ " \n",
+ " next_state, reward, done, info = env.step(move)\n",
+ " total_reward += reward\n",
+ " state = next_state\n",
+ " \n",
+ " if (i + 1) % 50 == 0: \n",
+ " env.render()\n",
+ " \n",
+ " if done:\n",
+ " break\n",
+ " \n",
+ " om = -om\n",
+ "\n",
+ " collected_rewards.append(total_reward)\n",
+ " \n",
+ " if (i + 1) % 50 == 0: \n",
+ " print(f\"Episode {i+1}, Total Reward: {total_reward}\")\n",
+ " average_reward = sum(collected_rewards) / len(collected_rewards)\n",
+ " print(f\"Average Reward: {average_reward}\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Обновлённая реализация проекта.\n",
+ "\n",
+ "### Описание проекта:\n",
+ "\n",
+ "Проект представляет собой обновленную реализацию среды для игры в крестики-нолики с использованием библиотеки Gymnasium и модели обучения с подкреплением, где агент обучается с помощью метода Q-learning.\n",
+ "\n",
+ "**Проект состоит из следующих компонентов:**\n",
+ "1. **Среда TicTacToeEnv:** \n",
+ " Реализует правила игры, управление ходами, проверку победных условий и завершения игры, а также визуализацию доски и генерацию доступных ходов.\n",
+ "2. **Агент TicTacToeAgent:** \n",
+ " Использует метод Q-learning для обучения, обновляя свою Q-таблицу на основе полученных наград. Агент выбирает ход с помощью epsilon-greedy стратегии, балансируя между исследованием и эксплуатацией.\n",
+ "3. **Цикл обучения:** \n",
+ " Выполняется симуляция игр (эпизодов), где агент играет против себя, обучаясь на основе своих действий. Ведется сбор статистики по победам, ничьим и общей награде для анализа эффективности обучения.\n",
+ "4. **Параметры обучения:** \n",
+ " Задаются параметры Q-learning (learning_rate, discount_factor, epsilon), которые регулируют процесс обучения агента.\n",
+ "5. **Сбор данных:** \n",
+ " Сохраняется статистика по каждому эпизоду, включая награды, победы и ничьи, для анализа прогресса обучения и улучшения стратегии агента.\n",
+ "\n",
+ "**Основные изменения в новой реализации:**\n",
+ "1. **Использование Q-learning для обучения агента:** \n",
+ " В отличие от предыдущей реализации, где игроки действовали случайным образом, теперь используется агент, обучающийся методом Q-learning. Агент обновляет свою Q-таблицу на основе наград и выбирает действия с использованием стратегии epsilon-greedy.\n",
+ "2. **Добавление класса TicTacToeAgent:** \n",
+ " В проект добавлен новый класс TicTacToeAgent, который реализует логику выбора действий на основе обучения с подкреплением. Агент выбирает ход с учетом предыдущего опыта и обновляет свою стратегию по мере игры.\n",
+ "3. **Цикл обучения с накоплением статистики:** \n",
+ " В новом цикле обучения агент обучается, играя против себя, с отслеживанием статистики по победам, ничьим и наградам за каждый эпизод. Это позволяет оценивать прогресс обучения и эффективность стратегии.\n",
+ "4. **Параметры обучения:** \n",
+ " Введены параметры обучения для настройки метода Q-learning (скорость обучения, коэффициент дисконтирования и вероятность случайного выбора действия), что позволяет гибко управлять процессом обучения агента.\n",
+ "5. **Рендеринг и вывод прогресса:** \n",
+ " Добавлена визуализация результатов симуляции всех эпизодов (победы игрока X, победы игрока O, ничьи) в виде графика.\n",
+ "\n",
+ "---"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Реализация среды:\n",
+ "\n",
+ "**Среда (environment)** – среда, в которой объект выполняет действия для решения задачи.\n",
+ "\n",
+ "Класс TicTacToeEnv представляет собой обновлённую реализацию среды для игры в крестики-нолики с использованием библиотеки Gymnasium. Среда предоставляет интерфейс для двух игроков, которые поочерёдно совершают ходы, стремясь выстроить три символа в ряд (по горизонтали, вертикали или диагонали). В новой версии упрощена структура представления состояния, улучшена обработка правил игры и повышена совместимость с алгоритмами обучения с подкреплением.\n",
+ "\n",
+ "Пространство действий (action_space) остаётся неизменным и представляет собой 9 дискретных значений, соответствующих клеткам игрового поля. Пространство наблюдений (observation_space) обновлено: теперь оно представлено одномерным массивом из 9 значений (np.ndarray), где 1 соответствует символу X, -1 – символу O, а 0 обозначает пустую клетку. Эта структура делает состояние более понятным и удобным для обработки.\n",
+ "\n",
+ "Игра начинается с вызова метода reset, который очищает доску (заполняет её нулями) и назначает первым ход игроку X (1). Метод step обрабатывает действия игроков, проверяя их легальность: попытка занять уже занятую клетку приводит к штрафу для текущего игрока, но игра продолжается. При успешном ходе состояние доски обновляется, проверяется наличие победной комбинации или ничьей. Победитель получает награду 1 (или -1), ничья приносит награду 0 для обоих игроков, после чего игра завершается.\n",
+ "\n",
+ "Метод check_winner проверяет наличие трёх одинаковых символов на одной из выигрышных линий (горизонталей, вертикалей или диагоналей). Если игрок выигрывает, возвращается соответствующий результат. Для визуализации состояния игры используется метод render, который выводит доску в текстовом формате как сетку из символов X, O и пробелов.\n",
+ "\n",
+ "**Основные изменения в новой реализации:**\n",
+ "- Упрощённое представление состояния доски через массив, что делает её более совместимой с алгоритмами обучения с подкреплением.\n",
+ "- Обновлённая логика обработки нелегальных ходов: вместо завершения игры игрок получает штраф, а игра продолжается.\n",
+ "- Вынесение проверки победы в отдельный метод check_winner, что улучшает читаемость и модульность кода.\n",
+ "- Удалён метод move_generator, так как доступные ходы обрабатываются напрямую внутри step.\n",
+ "\n",
+ "Эти изменения делают среду более универсальной, удобной для использования в проектах с обучением агентов и более гибкой в плане настроек игрового процесса."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 64,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import gymnasium as gym\n",
+ "from gymnasium import spaces\n",
+ "\n",
+ "\n",
+ "class TicTacToeEnv(gym.Env):\n",
+ " \"\"\"\n",
+ " Класс, реализующий среду для игры в крестики-нолики.\n",
+ " \n",
+ " Атрибуты:\n",
+ " action_space (spaces.Discrete): Пространство действий, где доступно 9 клеток.\n",
+ " observation_space (spaces.Box): Пространство наблюдений, представляющее состояние доски.\n",
+ " symbols (dict[int, str]): Словарь для отображения символов игроков (1 — \"X\", -1 — \"O\", 0 — пусто).\n",
+ " current_player (int): Игрок, который совершает текущий ход (1 для X, -1 для O).\n",
+ " board (np.ndarray): Состояние доски (1D массив из 9 элементов).\n",
+ " \n",
+ " Методы:\n",
+ " reset: Сбросить игру, вернув начальное состояние доски.\n",
+ " step: Сделать ход, обновив состояние игры.\n",
+ " check_winner: Проверить, есть ли победитель.\n",
+ " render: Отобразить текущую доску в консоли.\n",
+ " \"\"\"\n",
+ " \n",
+ " metadata: dict[str, list[str]] = {\"render_modes\": [\"ansi\"]}\n",
+ "\n",
+ " def __init__(self) -> None:\n",
+ " \"\"\"\n",
+ " Инициализация среды для игры в крестики-нолики.\n",
+ " Настройка пространства действий, пространства наблюдений и начальных значений.\n",
+ " \"\"\"\n",
+ " self.action_space = spaces.Discrete(9) # 9 клеток (от 0 до 8)\n",
+ " self.observation_space = spaces.Box(low=-1, high=1, shape=(9,), dtype=int) # 9 значений, от -1 до 1 (игроки X и O)\n",
+ " self.symbols: dict[int, str] = {1: \"X\", -1: \"O\", 0: \" \"} # Символы игроков и пустой символ\n",
+ "\n",
+ " self.reset()\n",
+ "\n",
+ " def reset(self, seed: int | None = None) -> np.ndarray:\n",
+ " \"\"\"\n",
+ " Сбросить состояние игры и начать новую партию.\n",
+ "\n",
+ " Параметры:\n",
+ " seed (int, optional): Сеед для случайных чисел (если используется).\n",
+ "\n",
+ " Возвращает:\n",
+ " np.ndarray: Начальное состояние доски (массив из 9 элементов).\n",
+ " \"\"\"\n",
+ " super().reset(seed=seed)\n",
+ " self.board = np.zeros(9, dtype=int) # Пустое поле\n",
+ " self.current_player = 1 # Ход первого игрока (X)\n",
+ " return self.board\n",
+ "\n",
+ " def step(self, action: int) -> tuple[np.ndarray, int, bool, bool]:\n",
+ " \"\"\"\n",
+ " Совершить ход в игре.\n",
+ "\n",
+ " Параметры:\n",
+ " action (int): Индекс клетки, в которую игрок хочет поставить свой символ.\n",
+ "\n",
+ " Возвращает:\n",
+ " tuple: \n",
+ " - np.ndarray: Обновленное состояние доски.\n",
+ " - int: Награда за ход (1, -1, или 0).\n",
+ " - bool: Флаг, указывающий на завершение игры.\n",
+ " - bool: Дополнительный флаг (не используется, всегда False).\n",
+ " \"\"\"\n",
+ " if self.board[action] != 0:\n",
+ " # Нелегальный ход (клетка уже занята)\n",
+ " reward: int = -self.current_player # Штраф за нелегальный ход: награду получает тот, кто НЕ совершил ошибку\n",
+ " self.current_player: int = -self.current_player # Смена хода к следующему игроку\n",
+ " return self.board, reward, False, False\n",
+ "\n",
+ " # Совершение хода\n",
+ " self.board[action] = self.current_player\n",
+ "\n",
+ " # Проверка на победу\n",
+ " if self.check_winner(self.current_player):\n",
+ " reward = self.current_player\n",
+ " terminated = True\n",
+ " elif np.all(self.board != 0):\n",
+ " # Ничья\n",
+ " reward = 0\n",
+ " terminated = True\n",
+ " else:\n",
+ " # Продолжение игры\n",
+ " reward = 0\n",
+ " terminated = False\n",
+ " self.current_player = -self.current_player # Смена хода\n",
+ "\n",
+ " return self.board, reward, terminated, False\n",
+ "\n",
+ " def check_winner(self, player: int) -> bool:\n",
+ " \"\"\"\n",
+ " Проверить, есть ли победитель для указанного игрока.\n",
+ "\n",
+ " Параметры:\n",
+ " player (int): Игрок, для которого проверяется победа (1 — X, -1 — O).\n",
+ "\n",
+ " Возвращает:\n",
+ " bool: True, если игрок выиграл, иначе False.\n",
+ " \"\"\"\n",
+ " winning_positions: list[tuple[int, int, int]] = [\n",
+ " (0, 1, 2), (3, 4, 5), (6, 7, 8), # Горизонтали\n",
+ " (0, 3, 6), (1, 4, 7), (2, 5, 8), # Вертикали\n",
+ " (0, 4, 8), (2, 4, 6), # Диагонали\n",
+ " ]\n",
+ " for positions in winning_positions:\n",
+ " if all(self.board[pos] == player for pos in positions):\n",
+ " return True\n",
+ " return False\n",
+ "\n",
+ " def render(self) -> None:\n",
+ " \"\"\"\n",
+ " Отобразить текущую доску в виде строк с символами X, O или пустыми клетками.\n",
+ " \"\"\"\n",
+ " board = self.board.reshape(3, 3)\n",
+ " print(\"\\n\".join(\" | \".join(self.symbols[cell] for cell in row) for row in board), end='\\n\\n')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Реализация агента:\n",
+ "\n",
+ "**Агент (agent)** – объект обучения, который выполняет действия в некоторой среде для получения вознаграждения, принимая решения на основе своих целей и информации, которую он получает.\n",
+ "\n",
+ "Класс TicTacToeAgent представляет собой агента для игры в крестики-нолики, использующего метод Q-learning для обучения и улучшения своей стратегии. Агент накапливает знания в процессе взаимодействия со средой, обновляя Q-таблицу, которая хранит оценки для каждой пары \"состояние-действие\". Он выбирает действия с использованием стратегии epsilon-greedy, что позволяет балансировать между исследованием новых возможностей и эксплуатацией уже известных действий с высокой наградой.\n",
+ "\n",
+ "**Основные компоненты агента:**\n",
+ "1. **Q-таблица** (q_table): Хранит значения для пар \"состояние-действие\", которые оценивают, насколько выгодно выполнить определённое действие в конкретном состоянии.\n",
+ "2. **Параметры обучения**:\n",
+ " - learning_rate (α): Скорость обновления Q-значений.\n",
+ " - discount_factor (γ): Коэффициент дисконтирования, определяющий важность будущих наград.\n",
+ " - epsilon: Вероятность случайного выбора действия, обеспечивающая баланс между исследованием и эксплуатацией.\n",
+ " \n",
+ "**Методы агента:**\n",
+ "- get_state_key: Преобразует текущее состояние доски в ключ для Q-таблицы.\n",
+ "- select_action: Реализует стратегию epsilon-greedy для выбора действия, либо случайного, либо с максимальным Q-значением.\n",
+ "- update: Обновляет Q-значения на основе полученной награды, используя формулу Q-learning.\n",
+ "\n",
+ "В отличие от предыдущей реализации, где игроки выбирали ходы случайным образом без обучения, новая версия с агентом использует Q-learning для улучшения своей стратегии. Агент адаптируется к среде, минимизируя ошибки и максимизируя долгосрочные награды. Он также сохраняет информацию о прошлых действиях через Q-таблицу и использует её для более эффективных решений в будущем. В то время как предыдущая версия игры не включала механизма обучения и все ходы были случайными, обновлённая реализация позволяет агенту развивать стратегию и достигать лучших результатов за счет обучения на основе полученного опыта."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 65,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class TicTacToeAgent:\n",
+ " \"\"\"\n",
+ " Агент для игры в крестики-нолики, использующий метод Q-learning для обучения.\n",
+ " \n",
+ " Атрибуты:\n",
+ " q_table (dict): Q-таблица для хранения значений (состояние, действие) и их оценок.\n",
+ " learning_rate (float): Скорость обучения (альфа).\n",
+ " discount_factor (float): Коэффициент дисконтирования (гамма).\n",
+ " epsilon (float): Вероятность случайного выбора действия (для баланса между исследованием и эксплуатацией).\n",
+ " \n",
+ " Методы:\n",
+ " get_state_key: Преобразует состояние в ключ для Q-таблицы.\n",
+ " select_action: Выбирает действие на основе epsilon-greedy стратегии.\n",
+ " update: Обновляет Q-значение с помощью Q-learning.\n",
+ " \"\"\"\n",
+ " \n",
+ " def __init__(self, learning_rate: float = 0.1, discount_factor: float = 0.9, epsilon: float = 0.1):\n",
+ " \"\"\"\n",
+ " Инициализация агента для игры в крестики-нолики.\n",
+ " \n",
+ " Параметры:\n",
+ " learning_rate (float): Скорость обучения (по умолчанию 0.1).\n",
+ " discount_factor (float): Коэффициент дисконтирования (по умолчанию 0.9).\n",
+ " epsilon (float): Вероятность случайного выбора действия (по умолчанию 0.1).\n",
+ " \"\"\"\n",
+ " self.q_table: dict[tuple, float] = {} # Q-таблица для хранения значений (состояние, действие)\n",
+ " self.learning_rate = learning_rate # Скорость обучения\n",
+ " self.discount_factor = discount_factor # Коэффициент дисконтирования\n",
+ " self.epsilon = epsilon # Вероятность случайного выбора действия (для epsilon-greedy)\n",
+ "\n",
+ " def get_state_key(self, state) -> tuple:\n",
+ " \"\"\"\n",
+ " Преобразует состояние (список из 9 элементов) в ключ для Q-таблицы.\n",
+ " \n",
+ " Параметры:\n",
+ " state: Текущее состояние игры (состояние доски).\n",
+ "\n",
+ " Возвращает:\n",
+ " tuple: Ключ для Q-таблицы, представляющий состояние.\n",
+ " \"\"\"\n",
+ " return tuple(state)\n",
+ "\n",
+ " def select_action(self, state, possible_actions: list[int]) -> int:\n",
+ " \"\"\"\n",
+ " Выбирает действие с использованием epsilon-greedy стратегии.\n",
+ "\n",
+ " Параметры:\n",
+ " state: Текущее состояние игры (состояние доски).\n",
+ " possible_actions (list[int]): Список возможных действий (клеток, куда можно поставить символ).\n",
+ "\n",
+ " Возвращает:\n",
+ " int: Индекс выбранного действия (клетки).\n",
+ " \"\"\"\n",
+ " state_key: tuple = self.get_state_key(state)\n",
+ "\n",
+ " # Исследование (с вероятностью epsilon) или эксплуатация (выбор действия с максимальным Q-значением)\n",
+ " if np.random.rand() < self.epsilon or state_key not in self.q_table:\n",
+ " # Исследование: случайный ход\n",
+ " return np.random.choice(possible_actions)\n",
+ " # Эксплуатация: выбор действия с максимальным Q-значением\n",
+ " return max(possible_actions, key=lambda a: self.q_table.get((state_key, a), 0))\n",
+ " \n",
+ " def update(self, state, action: int, reward: int, next_state, possible_actions: list[int], terminated: bool) -> None:\n",
+ " \"\"\"\n",
+ " Обновляет Q-таблицу на основе выполненного хода с использованием Q-learning.\n",
+ "\n",
+ " Параметры:\n",
+ " state: Текущее состояние игры (состояние доски).\n",
+ " action (int): Действие (выбранная клетка).\n",
+ " reward (int): Награда, полученная за выполнение хода.\n",
+ " next_state: Состояние доски после выполнения хода.\n",
+ " possible_actions (list[int]): Список возможных действий для следующего состояния.\n",
+ " terminated (bool): Флаг, указывающий на завершение игры.\n",
+ " \"\"\"\n",
+ " state_key: tuple = self.get_state_key(state)\n",
+ " next_state_key: tuple = self.get_state_key(next_state)\n",
+ "\n",
+ " # Инициализация Q-значения для (состояние, действие), если его нет в таблице\n",
+ " if (state_key, action) not in self.q_table:\n",
+ " self.q_table[(state_key, action)] = 0\n",
+ "\n",
+ " # Если игра завершена, будущая награда равна 0\n",
+ " if terminated:\n",
+ " future_reward = 0\n",
+ " else:\n",
+ " # Для текущего состояния находим максимальное Q-значение среди возможных действий в следующем состоянии\n",
+ " future_reward: float = max(self.q_table.get((next_state_key, a), 0) for a in possible_actions)\n",
+ "\n",
+ " # Формула для обновления Q-значения (TD-ошибка)\n",
+ " td_target: float = reward + self.discount_factor * future_reward\n",
+ " td_error: float = td_target - self.q_table[(state_key, action)]\n",
+ " self.q_table[(state_key, action)] += self.learning_rate * td_error"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Реализация основного цикла обучения:\n",
+ "\n",
+ "Основной цикл обучения выполняет симуляцию игры в крестики-нолики между агентом, использующим метод Q-learning, и средой в течение заданного числа эпизодов (episodes). В процессе обучения агент учится выбирать действия, ориентируясь на полученные награды и обновления своей Q-таблицы. Целью является улучшение стратегии игры на основе предыдущего опыта.\n",
+ "\n",
+ "**Описание логики цикла:**\n",
+ "1. **Инициализация:** \n",
+ " Перед началом эпизодов задаются параметры обучения: скорость обучения (learning_rate), коэффициент дисконтирования (discount_factor), вероятность случайного выбора действия (epsilon), и общее количество эпизодов (episodes). Также создаются объекты среды (env) и агента (agent), которые будут взаимодействовать друг с другом. Для отслеживания результатов создаются метрики статистики (например, количество побед и ничьих).\n",
+ "2. **Начало эпизода:**\n",
+ " - Каждый эпизод начинается с вызова метода env.reset(), который сбрасывает игровое поле и устанавливает начальное состояние.\n",
+ " - Для каждого эпизода инициализируются переменные для подсчета общей награды (total_reward), количества побед и ничьих. Флаг завершенности игры (done) устанавливается в False.\n",
+ "3. **Цикл ходов в одном эпизоде:**\n",
+ " - Для каждого хода агент выбирает действие, используя стратегию epsilon-greedy, что означает баланс между случайным выбором действия (для исследования) и выбором действия, которое на данный момент кажется оптимальным (эксплуатация).\n",
+ " - После выбора действия вызывается метод env.step(), который обновляет состояние игры, возвращает новую награду и флаг завершения игры (terminated).\n",
+ " - Агент обновляет свою Q-таблицу с помощью метода agent.update(), используя полученные награду и новое состояние.\n",
+ " - Если игра завершена (флаг terminated установлен в True), цикл завершения игры прерывается, и сохраняются данные для статистики.\n",
+ "4. **Сбор статистики:**\n",
+ " - В конце каждого эпизода сохраняются данные о общей награде, количестве побед и ничьих для игроков X и O в соответствующие списки статистики.\n",
+ " - Каждые 50 эпизодов выводится информация о текущем прогрессе, включая отображение доски через метод env.render(), а также выводится средняя награда за все эпизоды на текущий момент.\n",
+ "5. **Повторение:** \n",
+ " Цикл продолжается до завершения заданного количества эпизодов. По мере прохождения эпизодов агент постепенно улучшает свою стратегию игры, накапливая опыт и корректируя свои действия в соответствии с полученными результатами.\n",
+ "\n",
+ "**Основные изменения в новой реализации:**\n",
+ "1. **Использование Q-learning:** В новой версии агент обучается с помощью Q-learning, обновляя свою Q-таблицу на основе полученных наград, что позволяет улучшать стратегию игры.\n",
+ "2. **Стратегия выбора хода:** Агент использует **epsilon-greedy** стратегию для выбора ходов, что позволяет балансировать между исследованием новых ходов и эксплуатацией оптимальных действий.\n",
+ "3. **Обновление Q-таблицы:** Каждый ход агента сопровождается обновлением Q-значений на основе полученной награды и информации о будущем состоянии, улучшая стратегию игры.\n",
+ "4. **Отслеживание статистики:** Ведется подробная статистика по каждому эпизоду (победы, ничьи, общая награда), что позволяет отслеживать прогресс обучения."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " | | \n",
+ "X | | \n",
+ " | | \n",
+ "\n",
+ "O | | \n",
+ "X | | \n",
+ " | | \n",
+ "\n",
+ "O | | \n",
+ "X | | \n",
+ "X | | \n",
+ "\n",
+ "O | | \n",
+ "X | | \n",
+ "X | | O\n",
+ "\n",
+ "O | | X\n",
+ "X | | \n",
+ "X | | O\n",
+ "\n",
+ "O | | X\n",
+ "X | | O\n",
+ "X | | O\n",
+ "\n",
+ "O | | X\n",
+ "X | | O\n",
+ "X | X | O\n",
+ "\n",
+ "O | O | X\n",
+ "X | | O\n",
+ "X | X | O\n",
+ "\n",
+ "O | O | X\n",
+ "X | X | O\n",
+ "X | X | O\n",
+ "\n",
+ "Эпизод 50, Общая награда: 1\n",
+ "Средняя награда: 0.18\n",
+ "\n",
+ " | | \n",
+ " | X | \n",
+ " | | \n",
+ "\n",
+ " | | \n",
+ "O | X | \n",
+ " | | \n",
+ "\n",
+ " | | X\n",
+ "O | X | \n",
+ " | | \n",
+ "\n",
+ "O | | X\n",
+ "O | X | \n",
+ " | | \n",
+ "\n",
+ "O | | X\n",
+ "O | X | \n",
+ " | X | \n",
+ "\n",
+ "O | | X\n",
+ "O | X | \n",
+ "O | X | \n",
+ "\n",
+ "Эпизод 100, Общая награда: -1\n",
+ "Средняя награда: 0.35\n",
+ "\n",
+ " | | X\n",
+ " | | \n",
+ " | | \n",
+ "\n",
+ " | | X\n",
+ " | O | \n",
+ " | | \n",
+ "\n",
+ " | X | X\n",
+ " | O | \n",
+ " | | \n",
+ "\n",
+ " | X | X\n",
+ " | O | \n",
+ "O | | \n",
+ "\n",
+ " | X | X\n",
+ " | O | \n",
+ "O | | X\n",
+ "\n",
+ " | X | X\n",
+ " | O | O\n",
+ "O | | X\n",
+ "\n",
+ "X | X | X\n",
+ " | O | O\n",
+ "O | | X\n",
+ "\n",
+ "Эпизод 150, Общая награда: 1\n",
+ "Средняя награда: 0.35333333333333333\n",
+ "\n",
+ " | | \n",
+ " | | X\n",
+ " | | \n",
+ "\n",
+ " | | \n",
+ " | O | X\n",
+ " | | \n",
+ "\n",
+ " | | \n",
+ "X | O | X\n",
+ " | | \n",
+ "\n",
+ "O | | \n",
+ "X | O | X\n",
+ " | | \n",
+ "\n",
+ "O | | \n",
+ "X | O | X\n",
+ "X | | \n",
+ "\n",
+ "O | | O\n",
+ "X | O | X\n",
+ "X | | \n",
+ "\n",
+ "O | | O\n",
+ "X | O | X\n",
+ "X | | X\n",
+ "\n",
+ "O | O | O\n",
+ "X | O | X\n",
+ "X | | X\n",
+ "\n",
+ "Эпизод 200, Общая награда: -1\n",
+ "Средняя награда: 0.405\n",
+ "\n",
+ " | | \n",
+ " | | X\n",
+ " | | \n",
+ "\n",
+ "O | | \n",
+ " | | X\n",
+ " | | \n",
+ "\n",
+ "O | | \n",
+ " | | X\n",
+ "X | | \n",
+ "\n",
+ "O | | \n",
+ " | O | X\n",
+ "X | | \n",
+ "\n",
+ "O | | \n",
+ "X | O | X\n",
+ "X | | \n",
+ "\n",
+ "O | | \n",
+ "X | O | X\n",
+ "X | O | \n",
+ "\n",
+ "O | X | \n",
+ "X | O | X\n",
+ "X | O | \n",
+ "\n",
+ "O | X | \n",
+ "X | O | X\n",
+ "X | O | O\n",
+ "\n",
+ "Эпизод 250, Общая награда: -1\n",
+ "Средняя награда: 0.408\n",
+ "\n",
+ " | | \n",
+ " | | \n",
+ " | | X\n",
+ "\n",
+ " | | \n",
+ "O | | \n",
+ " | | X\n",
+ "\n",
+ " | | \n",
+ "O | X | \n",
+ " | | X\n",
+ "\n",
+ " | O | \n",
+ "O | X | \n",
+ " | | X\n",
+ "\n",
+ "X | O | \n",
+ "O | X | \n",
+ " | | X\n",
+ "\n",
+ "Эпизод 300, Общая награда: 1\n",
+ "Средняя награда: 0.38666666666666666\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Параметры обучения\n",
+ "learning_rate: float = 0.1 # Скорость обучения (alpha)\n",
+ "discount_factor: float = 0.9 # Коэффициент дисконтирования (gamma)\n",
+ "epsilon: float = 0.1 # Вероятность случайного выбора действия для исследования (epsilon)\n",
+ "episodes: int = 300 # Количество эпизодов обучения\n",
+ "\n",
+ "# Инициализация среды и агента\n",
+ "env: TicTacToeEnv = TicTacToeEnv() # Создание экземпляра среды для игры в крестики-нолики\n",
+ "agent: TicTacToeAgent = TicTacToeAgent(learning_rate, discount_factor, epsilon) # Создание агента для игры с Q-learning\n",
+ "\n",
+ "# Инициализация метрик для отслеживания прогресса\n",
+ "statistics: dict[str, list[int]] = {\n",
+ " \"Episode\": [], # Номер эпизода\n",
+ " \"Total Reward\": [], # Общая награда за эпизод\n",
+ " \"Wins_X\": [], # Количество побед для игрока X\n",
+ " \"Wins_O\": [], # Количество побед для игрока O\n",
+ " \"Draws\": [], # Количество ничьих\n",
+ "}\n",
+ "\n",
+ "# Основной цикл обучения\n",
+ "for episode in range(episodes):\n",
+ " state = env.reset() # Сброс среды (начало нового эпизода)\n",
+ " done: bool = False # Флаг, который указывает, завершена ли игра\n",
+ " total_reward: int = 0 # Переменная для хранения общей награды за эпизод\n",
+ " wins_X: int = 0 # Переменная для подсчета побед игрока X\n",
+ " wins_O: int = 0 # Переменная для подсчета побед игрока O\n",
+ " draws: int = 0 # Переменная для подсчета ничьих\n",
+ "\n",
+ " while not done: # Пока игра не завершена\n",
+ " # Получаем список возможных действий (пустые клетки на доске)\n",
+ " possible_actions: list[int] = [i for i in range(9) if state[i] == 0]\n",
+ " \n",
+ " # Агент выбирает действие с использованием epsilon-greedy стратегии\n",
+ " action: int = agent.select_action(state, possible_actions)\n",
+ "\n",
+ " # Совершаем ход в среде, получаем новое состояние, награду и статус завершенности игры\n",
+ " next_state, reward, terminated, truncated = env.step(action)\n",
+ "\n",
+ " # Обновление Q-таблицы агента на основе выполненного хода\n",
+ " agent.update(state, action, reward, next_state, possible_actions, terminated)\n",
+ "\n",
+ " # Обновление статистики\n",
+ " total_reward += reward # Суммируем награду за ход\n",
+ " if reward == 1: # Победа для игрока X\n",
+ " wins_X += 1\n",
+ " elif reward == -1: # Победа для игрока O\n",
+ " wins_O += 1\n",
+ " elif reward == 0 and terminated: # Ничья\n",
+ " draws += 1\n",
+ "\n",
+ " # Обновляем состояние для следующего шага\n",
+ " state = next_state\n",
+ " done = terminated # Игра завершается, если флаг \"terminated\" равен True\n",
+ " \n",
+ " # Выводим отображение игры каждые 50 эпизодов\n",
+ " if (episode + 1) % 50 == 0:\n",
+ " env.render()\n",
+ "\n",
+ " # Сохраняем статистику по эпизоду\n",
+ " statistics[\"Episode\"].append(episode + 1) # Номер эпизода\n",
+ " statistics[\"Total Reward\"].append(total_reward) # Общая награда за эпизод\n",
+ " statistics[\"Wins_X\"].append(wins_X) # Победы для игрока X\n",
+ " statistics[\"Wins_O\"].append(wins_O) # Победы для игрока O\n",
+ " statistics[\"Draws\"].append(draws) # Ничьи\n",
+ "\n",
+ " # Выводим прогресс каждые 50 эпизодов\n",
+ " if (episode + 1) % 50 == 0:\n",
+ " print(f\"Эпизод {episode + 1}, Общая награда: {total_reward}\")\n",
+ " # Рассчитываем среднюю награду\n",
+ " average_reward: float = sum(statistics[\"Total Reward\"]) / len(statistics[\"Total Reward\"])\n",
+ " print(f\"Средняя награда: {average_reward}\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Итоги:\n",
+ "\n",
+ "**Описание графика:** \n",
+ "График демонстрирует кумулятивный подсчет побед для игроков X и O, а также количество ничьих по мере прохождения обучающих эпизодов игры в крестики-нолики. Каждая линия на графике отражает количество побед или ничьих, накопленное за всё время обучения, что позволяет отслеживать прогресс и эффективность стратегии каждого игрока.\n",
+ "\n",
+ "- **Красная линия (Победы X):** Показывает кумулятивное количество побед игрока X (крестики) за каждый эпизод. Это отражает, как стратегия игрока X улучшалась или стабилизировалась по мере обучения.\n",
+ "- **Синяя линия (Победы O):** Показывает кумулятивное количество побед игрока O (нолики), отображая успешность его стратегии.\n",
+ "- **Зеленая линия (Ничьи):** Показывает количество ничьих, что помогает оценить, как часто игры заканчивались без победителя.\n",
+ "\n",
+ "График позволяет визуально оценить:\n",
+ "1. **Эффективность обучения:** Как меняется количество побед для каждого игрока с каждым эпизодом. Это может показать, насколько хорошо агент учится и насколько улучшилась его стратегия.\n",
+ "2. **Равновесие игры:** Наличие линий, которые растут с похожей скоростью, может указывать на сбалансированность игры между игроками X и O. Преобладание одной из линий может свидетельствовать о доминировании одной из стратегий.\n",
+ "3. **Показатели ничьих:** Анализ линии ничьих помогает оценить, насколько часто игра заканчивается без победителя, что может быть результатом сбалансированных стратегий обоих игроков.\n",
+ "\n",
+ "**Итоги выполненной работы:**\n",
+ "1. **Разработка и обучение агентов:** Создан агент, использующий метод Q-learning для игры в крестики-нолики, который обучается на основе взаимодействия с игровой средой. Агенты оптимизируют свои стратегии для достижения победы или ничьей. \n",
+ "2. **Реализация среды для игры:** Разработана среда для игры в крестики-нолики с использованием библиотеки Gymnasium. Среда включает в себя управление состоянием игры, проверку победных условий и обработку действий игроков.\n",
+ "3. **Анализ эффективности стратегий:** Для каждого эпизода отслеживается количество побед и ничьих, что помогает в анализе прогресса обучения и эффективности стратегий обоих игроков.\n",
+ "4. **Визуализация прогресса обучения:** Построен кумулятивный график, который демонстрирует, как с каждым эпизодом увеличивается количество побед для игроков и ничьих. График позволяет увидеть, насколько эффективно развивается стратегия агентов и их производительность в процессе обучения.\n",
+ "5. **Использование обучения с подкреплением:** В проекте применен алгоритм Q-learning, который позволил агентам развивать свои стратегии через опыт, обновляя свою Q-таблицу на основе получаемых наград."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAJOCAYAAABm7rQwAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC9p0lEQVR4nOzdd3gU1dvG8XvTSUIIoYUIhF6lS0d6R6QpgpTQlaaCoqJ0FESqUgXpRZAivqLSpCkiooCIItIRIfQWSuq8f8wvK0sSyIYku0m+n+uayz1zzs4+s3sSw7OnWAzDMAQAAAAAAACkIhdHBwAAAAAAAICMh6QUAAAAAAAAUh1JKQAAAAAAAKQ6klIAAAAAAABIdSSlAAAAAAAAkOpISgEAAAAAACDVkZQCAAAAAABAqiMpBQAAAAAAgFRHUgoAAAAAAACpjqQUAAAAAAAAUh1JKQBAilm4cKEsFot++eWXOHVz586VxWJRq1atFB0d7YDoAAAAADgSSSkAQKr74osv1KdPHz399NNasWKFXF1dHR0SAAAAgFRGUgoAkKq2b9+uDh06qGTJkvrqq6/k5eXl6JAAAAAAOABJKQBAqjlw4IBatmyp3Llza+PGjcqSJYu1bsSIEXJ3d9elS5fiPK93797y9/fXvXv3JEn58+eXxWLRa6+9Fqdt48aNZbFY9Mwzz0iSwsLC5OPjo1dffTVO27Nnz8rV1VXjxo2zOV+nTh1ZLJY4x8KFC23azZo1S08++aS8vb1t2q1evfqh17n/iGWxWDRy5Eib60+YMEEWi0V16tSxntu+fbvN8z09PVW0aFGNGzdOhmFY240cOVIWi0WXL1+2ueYvv/wS514OHjyorl27qmDBgvLy8lJgYKC6d++uK1euxHnPtm3bpqefflpZs2a1iaN///5x2t7vwbgfPLp27WptGzvtc+fOnXrppZeULVs2+fn5qUuXLrp27Vqca8+cOVOlSpWSp6engoKC1K9fP12/ft2mTZ06dWzeR0nau3dvnM/hypUratq0qfLkySNPT0/lzp1bHTt21OnTp61tTp06ZRO7u7u78ufPr8GDBysiIsLmNU6cOKHnn39eAQEB8vb2VtWqVfX111/H+x7FfmYPHg/Gndh7eVTfu/8a4eHhGjFihAoXLixPT0/lzZtXb775psLDw+PEGfv5POx6D5u6e7+uXbvK19c3zvnVq1fLYrFo+/btNudXrVqlihUrKlOmTMqePbs6deqkf//9N87z//rrL7Vr1045cuRQpkyZVKxYMb377ruSEn6f7z9iX7dOnTp68sknH3oPCXnUz/+pU6ds2iemHz/o1q1b6tmzp4KDg+Xp6ak8efLo5Zdf1oULF2zaJfSZxR73/+6JfX/uFxYWpsDAwDifycsvv6wiRYrI29tbAQEBqlevnr7//vs4cdpzb4/6TCQpIiJCw4cPV8WKFZUlSxb5+Pjo6aef1rZt22yudf/P6rp162zq7t27Z/09NnHixITfZABAinJzdAAAgIzh+PHjatKkiTw9PbVx40blzp3bpr5z584aPXq0Vq5caZPgiIiI0OrVq9W2bVubUVVeXl5atmyZJkyYIHd3d0lmkum7776zaefr66vWrVtr5cqVmjx5ss1Uwc8++0yGYahjx45x4i1evLj1H7GXL1/WwIEDbepXrlypvn37qk6dOhowYIB8fHx0+PBhjR071trm3XffVc+ePW2u0bt3bz399NOPfL+uX78eJ1l2v3feeUclSpTQ3bt3tXLlSr3zzjvKmTOnevTo8chrP2jz5s06ceKEunXrpsDAQP3xxx+aM2eO/vjjD/3000/Wf6CePHlSzZs3V+7cuTV8+HDlyJFDkvnZJdYrr7yiSpUq2ZyLfY8e1L9/f/n7+2vkyJE6cuSIZs2apdOnT1sTXJL5D+hRo0apQYMG6tOnj7Xd3r17tWvXLmvfiM9bb70V51xERIQyZ86sV199VdmyZdPx48c1bdo0HTx4UL///rtN29jPMjw8XBs3btTEiRPl5eWlMWPGSJIuXLig6tWr686dO3rllVeULVs2LVq0SM8++6xWr16t1q1bxxvXrFmzrImaIUOGJBj/o+5lyZIl1sfff/+95syZoylTpih79uySpFy5ckmSYmJi9Oyzz+qHH35Q7969VaJECf3++++aMmWK/v777zj/mI91/7Xef//9RMX5OBYuXKhu3bqpUqVKGjdunC5cuKCPPvpIu3bt0v79++Xv7y/JTLI+/fTTcnd3V+/evZU/f34dP35cX331ld5//321adNGhQsXtl534MCBKlGihHr37m09V6JEiWSJOU+ePHF+jr/55ht99tlnNueS2o+vXr2qgwcPqmfPngoMDNSxY8c0e/ZsbdiwQT///LNy5sxp03706NEq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DxJSKYaklJ0MQ7p92zGHYST//fz6669q166d2rdvr99//10jR47UsGHDrMmfS5cuKTIyUkuWLNGsWbO0f/9+lStXTk2aNNH58+clSdevX1e9evVUvnx5/fLLL9qwYYMuXLigdu3axfuaR44c0fnz5zV16tRku4+rV69q48aN6tu3rzJlymRTFxgYqI4dO2rlypUyUuJNBAAAAAA4p1u3pOvX4x4XL5q76T37rHTpklS2rPTrr9KAAZILqZLUwvQ9O925I/n6Oua1w8IkH5/kvebkyZNVv359DRs2TJJUtGhR/fnnn5owYYK6du2qmJgYSdKECRPUrFkzSdLMmTO1detWzZgxQ++9956mT5+u8uXLa+zYsdbrzp8/X3nz5tXff/+tokWLSpLCw8MlSU888YR8fHySdU2qo0ePyjAMlShRIt76EiVK6Nq1a7p06ZJy5syZbK8LAAAAAHBC//4r9eghbdz48HYWi/T669J775kjpZCqSP9lcIcPH1aNGjVsztWoUUNHjx5VdHS0zblYLi4uql69uv78809J0m+//aZt27bJ19fXehQvXlySdPz4cevzrly5Ijc3N3l7ez80purVqytz5szKmzevXnjhBZ09ezbR98NIKAAAAADI4NaskcqUeXRCqmBBacsWacIEElIOwkgpO3l7myOWHPXaqS1r1qwJ1lksFklSWFiYWrRoofHjx8dpkzt3buvjEydOKDg42Pq8hKxcuVIlSpRQaGioXnnlFb388stav379Q59TuHBhWSwWHT58WK1bt45Tf/jwYWXNmlU5cuR46HUAAAAAAGnUrVvSK69IsWsRV6woLVkiFS4cf3s3N3OkFByGpJSdLJbkn0LnSCVKlNCuXbtszu3atUtFixaVq6urChUqJDc3N+3atUvBwcGSpJiYGP3444964YUXJEkVKlTQmjVrlD9/frm5JdylduzYoaeffvqRMeXNm1eFCxdW4cKF1aNHD40bN+6Rz8mWLZsaNmyomTNnauDAgTbrSoWGhmrZsmXq0qXLIxNiAAAAAIA06Mcfpc6dpRMnzH+4DxkijRgheXg4OjI8BNP3MrjXX39d3333ncaMGaO///5bixYt0vTp0/XGG29Iknx9fdWrVy8NHjxY33zzjQ4fPqy+ffvq3Llz6tu3ryRz17urV6+qQ4cO2rt3r44fP66NGzeqW7duio6O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7NnjpqhQ2rVr106xsbFq2LChjh49qqlTp+qmm27Sr7/+qmPHjikgIEDh4eH5nlO5cmUdO3bMPQGjVDs37gr7HD137tixY4qMjMx33s/PTxEREYxbuEy3bt109913q06dOjpw4ICefvppde/eXfHx8fL19WWcwmXsdrueeOIJ3XDDDWratKkkFen7+7Fjxwr9rD13DnCkwsapJPXr10+1atVSVFSUtm/frqeeekp79+7V559/LolxCufbsWOH2rdvr6ysLJUtW1ZLly5V48aNtW3btlL/OUpSCoBDdO/ePe9x8+bN1a5dO9WqVUuffPKJgoOD3RgZAHive++9N+9xs2bN1Lx5c9WrV09r1qxRp06d3BgZSpvhw4fr119/zVcvEvA0Fxqnf6+z16xZM1WtWlWdOnXSgQMHVK9ePVeHiVKoYcOG2rZtm5KTk/XZZ59p0KBBiouLc3dYHoHle16oYsWK8vX1LVCR//jx46pSpYqbogLyCw8PV4MGDbR//35VqVJFOTk5SkpKyncNYxbucm7cXexztEqVKgU2j8jNzdWZM2cYt3CbunXrqmLFitq/f78kxilcIzo6Wl9//bV++OEHVa9ePe94Ub6/V6lSpdDP2nPnAEe50DgtTLt27SQp32cp4xTOFBAQoPr166t169aaMWOGWrRooddee43PUZGU8koBAQFq3bq1Vq1alXfMbrdr1apVat++vRsjA85LS0vTgQMHVLVqVbVu3Vr+/v75xuzevXuVmJjImIVb1KlTR1WqVMk3JlNSUrRx48a8Mdm+fXslJSVpy5YtedesXr1adrs974dZwNUOHz6s06dPq2rVqpIYp3AuwzAUHR2tpUuXavXq1apTp06+80X5/t6+fXvt2LEjX/J05cqVCg0NVePGjV3zQlCiXWqcFmbbtm2SlO+zlHEKV7Lb7crOzuZzVGL3PW/10UcfGYGBgUZsbKyxa9cu4+GHHzbCw8PzVeQHXGn06NHGmjVrjISEBGP9+vXGbbfdZlSsWNE4ceKEYRiGMXToUKNmzZrG6tWrjc2bNxvt27c32rdv7+aoUZKlpqYaW7duNbZu3WpIMl555RVj69atxh9//GEYhmHMnDnTCA8PN7788ktj+/btRq9evYw6deoYmZmZeX1069bNuOaaa4yNGzca69atM6666iqjb9++7npJKIEuNk5TU1ONMWPGGPHx8UZCQoLx/fffG61atTKuuuoqIysrK68Pximc5dFHHzXCwsKMNWvWGEePHs37ysjIyLvmUt/fc3NzjaZNmxpdunQxtm3bZnz77bdGpUqVjPHjx7vjJaEEutQ43b9/vzFt2jRj8+bNRkJCgvHll18adevWNW6++ea8PhincKZx48YZcXFxRkJCgrF9+3Zj3LhxhsViMb777jvDMPgcJSnlxd544w2jZs2aRkBAgNG2bVtjw4YN7g4JpVifPn2MqlWrGgEBAUa1atWMPn36GPv37887n5mZaQwbNswoX768ERISYtx1113G0aNH3RgxSroffvjBkFTga9CgQYZhGIbdbjcmTpxoVK5c2QgMDDQ6depk7N27N18fp0+fNvr27WuULVvWCA0NNe6//34jNTXVDa8GJdXFxmlGRobRpUsXo1KlSoa/v79Rq1YtY8iQIQX+AMU4hbMUNjYlGe+++27eNUX5/v77778b3bt3N4KDg42KFSsao0ePNqxWq4tfDUqqS43TxMRE4+abbzYiIiKMwMBAo379+sbYsWON5OTkfP0wTuEsDzzwgFGrVi0jICDAqFSpktGpU6e8hJRh8DlqMQzDcN28LAAAAAAAAICaUgAAAAAAAHADklIAAAAAAABwOZJSAAAAAAAAcDmSUgAAAAAAAHA5klIAAAAAAABwOZJSAAAAAAAAcDmSUgAAAAAAAHA5klIAAAAAAABwOZJSAAAAAAAAcDmSUgAAAAAAAHA5klIAAADFYLVaFRsbqxtvvFGVKlVScHCwmjdvrhdeeEE5OTnuDg8AAMBrWAzDMNwdBAAAgLfYtm2bRo8erWHDhumaa65RVlaWduzYoSlTpqhq1ar6v//7P/n7+7s7TAAAAI/HTCkAAIBiaNq0qVatWqXevXurbt26aty4sfr06aO1a9fq119/VUxMjCTJYrEU+vXEE0/k9VW7du286yVp1apVslgsuvPOO/OODRw4UJGRkQoMDFTdunX10ksv5Yvnv//9r5o0aaLAwEDVrl1bL7/8coGYY2NjC8TRsmXLvPNr1qyRxWJRUlJSgecmJSXJYrFozZo1l/GvBQAAcGEkpQAAAIrBz8+v0OOVKlXS3XffrcWLF+cde/fdd3X06NG8r/bt21+wX7vdrtGjR6ts2bL5jt977736/vvvtW/fPj3//PMaP3681q5dK0nasmWL7rnnHt177715s7UmTpyo2NjYAv2HhobmxTF69OjLeOUAAACOVfhPVQAAALioJk2a6I8//sh3zGq1ytfXN68dHh6uKlWq5LUDAgIu2N+iRYuUnZ2tXr16KS0tLe94z5498x6fOXNGfn5+stlskqRXXnlFnTp10sSJEyVJDRo00K5du/Tiiy9q8ODBec/Lzs5WQEBAXiz/THwBAAC4AzOlAAAALsPy5cu1bdu2fF+DBg26rL4yMjL0zDPPaNasWYXOxBo6dKiCg4N17bXXauLEibr11lslSbt379YNN9yQ79obbrhB+/bty0tcSdLp06cVGhp6yTiqV6+ucuXKqU6dOhoyZIiSk5Mv6/UAAAAUBUkpAACAy1CrVi3Vr18/39eBAwfUoEGDYvf14osvqmHDhrr99tsLPT9t2jRt2bJFMTExeuWVV7R79+5i9X/w4EHVqVPnktf9+OOP2rp1qxYsWKCVK1dqwoQJxboPAABAcbB8DwAAoBjOnDkjf39/lStXLt/xzZs364cfftDMmTOL1d/Ro0c1b948xcXFXfCayMhIRUZGqnHjxnr77bf1zTff6Oqrr9bVV1+t9evX57t2/fr1atCgQb5lhGvXrlX//v0vGUudOnUUHh6u+vXr6z//+Y/i4+OL9VoAAACKg6QUAABAMSQmJqp37956+umndcstt8jHx0fr16/XuHHjdNNNN+XbXa8o5syZo969e+uaa64pcC4pKUlffPGFrrvuOgUEBOjrr7/Wjh078q4dPXq02rRpo2effVZ9+vRRfHy8Zs+erblz50qSMjMztXDhQh04cEDdu3fXsWPHJElpaWnKzc3VmTNnFBERkXe/7OxsZWVl6ffff9eKFSt04403Xua/EgAAwKWRlAIAACiGpk2bavLkyVqwYIGeeuoppaenq379+oqOjtaoUaMuWsy8MHa7Xc8//3yh5wzDUGxsrEaOHKns7GzVrVtX8+bNU6dOnSRJrVq10ieffKJJkybp2WefVdWqVTVt2rS8Iucff/yxHnvsMUlSu3btCvR/9913a82aNXntc4XQK1asqC5dumjGjBnFei0AAADFYTEMw3B3EAAAAHC82NhYrVmzRrGxsQXObdu2TU888US+pBQAAIArUegcAACghAoODlZYWFih5/z9/fMt3QMAAHA1ZkoBAAAAAADA5ZgpBQAAAAAAAJcjKQUAAAAAAACXIykFAAAAAAAAlyMpBQAAAAAAAJcjKQUAAAAAAACXIykFAAAAAAAAlyMpBQAAAAAAAJcjKQUAAAAAAACXIykFAAAAAAAAl/t/JndcS34QHz4AAAAASUVORK5CYII=",
+ "text/plain": [
+ "