mirosh111000

TicTacToe

May 2nd, 2024
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.12 KB | None | 0 0
  1. import pandas as pd
  2. import numpy as np
  3. import random
  4. import matplotlib.pyplot as plt
  5. import pygame
  6. import sys
  7.  
  8. class TicTacToe:
  9.     def __init__(self, board_size=3):
  10.         self.board_size = board_size
  11.         self.board = np.zeros((board_size, board_size), dtype=int)  # Ігрове поле: 0 - порожньо, 1 - хрестик, 2 - нулик
  12.         self.current_player = 1  # Починає хрестик
  13.        
  14.    
  15.     def draw_board(self, window, cell_size, window_height, window_width):
  16.         window.fill((255, 255, 255))  # Заповнюємо вікно білим кольором
  17.         # Малюємо лінії, що розділяють комірки
  18.         for i in range(1, self.board_size):
  19.             pygame.draw.line(window, (0, 0, 0), (0, i * cell_size), (window_width, i * cell_size))
  20.             pygame.draw.line(window, (0, 0, 0), (i * cell_size, 0), (i * cell_size, window_height))
  21.         # Малюємо символи на ігровому полі
  22.         symbols = {2: 'O', 0: ' ', 1: 'X'}
  23.         for i in range(self.board_size):
  24.             for j in range(self.board_size):
  25.                 font = pygame.font.Font(None, 100)
  26.                 text = font.render(symbols[self.board[i][j]], True, (0, 0, 0))
  27.                 text_rect = text.get_rect(center=(j * cell_size + cell_size // 2, i * cell_size + cell_size // 2))
  28.                 window.blit(text, text_rect)
  29.                
  30.         return window
  31.                
  32.  
  33.     def exe_play(self, agents, vs='agent', player_side='random'):
  34.         def we_get_winner(winner):
  35.             if winner == 1:
  36.                 return 'Переміг хрестик!'
  37.             elif winner == 2:
  38.                 return 'Переміг нулик!'
  39.             elif winner == 0:
  40.                 return 'Нічия!'
  41.             return '-'
  42.            
  43.         pygame.init()
  44.  
  45.         window_width = 300
  46.         window_height = 300
  47.         cell_size = window_width // self.board_size
  48.         window = pygame.display.set_mode((window_width, window_height))
  49.         pygame.display.set_caption("Хрестики-нулики")
  50.  
  51.         agents[0].epsilon = 0
  52.         agents[1].epsilon = 0
  53.         side = ['X', 'O']
  54.        
  55.         self.__init__(board_size=self.board_size)
  56.         window = self.draw_board(window=window, cell_size=cell_size, window_height=window_height, window_width=window_width)
  57.         pygame.display.flip()
  58.        
  59.         if player_side == 'random' and vs == 'agent':
  60.             if np.random.rand() > 0.5:
  61.                 agent_side = 0
  62.                 move = agents[agent_side].choose_move(self)
  63.                 pygame.time.wait(1000)
  64.                 self.make_move(move)
  65.                 window = self.draw_board(window=window, cell_size=cell_size, window_height=window_height, window_width=window_width)
  66.                 pygame.display.flip()
  67.             else:
  68.                 agent_side = 1
  69.                
  70.         if player_side == 'O' and vs == 'agent':
  71.             agent_side = 0
  72.             move = agents[agent_side].choose_move(self)
  73.             pygame.time.wait(1000)
  74.             self.make_move(move)
  75.             window = self.draw_board(window=window, cell_size=cell_size, window_height=window_height, window_width=window_width)
  76.             pygame.display.flip()
  77.            
  78.         if player_side == 'X' and vs == 'agent':
  79.             agent_side = 1
  80.        
  81.         pygame.display.set_caption(f"Хід: {side[self.current_player-1]}")
  82.         while True:
  83.             for event in pygame.event.get():
  84.                 if event.type == pygame.QUIT:
  85.                     pygame.quit()
  86.                     sys.exit()
  87.                     return
  88.                 if vs == 'player':
  89.                     if event.type == pygame.MOUSEBUTTONDOWN:
  90.                         mouse_pos = pygame.mouse.get_pos()
  91.                         row = mouse_pos[1] // cell_size
  92.                         col = mouse_pos[0] // cell_size
  93.                         move = (row, col)
  94.                         if move in self.available_moves():
  95.                             self.make_move(move)
  96.                             pygame.display.set_caption(f"Хід: {side[self.current_player-1]}")
  97.                             window = self.draw_board(window=window, cell_size=cell_size, window_height=window_height, window_width=window_width)
  98.                             pygame.display.flip()
  99.                             winner = self.check_winner()
  100.                             winner = we_get_winner(winner)
  101.                             if winner != '-':
  102.                                 pygame.display.set_caption(f"{winner}")
  103.                                 pygame.time.wait(2000)
  104.                                 pygame.quit()
  105.                                 return
  106.                                
  107.                 if vs == 'agent':
  108.                     if event.type == pygame.MOUSEBUTTONDOWN:
  109.                         mouse_pos = pygame.mouse.get_pos()
  110.                         row = mouse_pos[1] // cell_size
  111.                         col = mouse_pos[0] // cell_size
  112.                         move = (row, col)
  113.                         if move in self.available_moves():
  114.                             self.make_move(move)
  115.                             pygame.display.set_caption(f"Хід: {side[self.current_player-1]}")
  116.                             window = self.draw_board(window=window, cell_size=cell_size, window_height=window_height, window_width=window_width)
  117.                             pygame.display.flip()
  118.                             winner = self.check_winner()
  119.                             winner = we_get_winner(winner)
  120.                             if winner != '-':
  121.                                 pygame.display.set_caption(f"{winner}")
  122.                                 pygame.time.wait(2000)
  123.                                 pygame.quit()
  124.                                 return
  125.                            
  126.                             # Хід комп'ютера
  127.                             move = agents[agent_side].choose_move(self)
  128.                             pygame.time.wait(1000)
  129.                             self.make_move(move)
  130.                             pygame.display.set_caption(f"Хід: {side[self.current_player-1]}")
  131.                             window = self.draw_board(window=window, cell_size=cell_size, window_height=window_height, window_width=window_width)
  132.                             pygame.display.flip()
  133.                             winner = self.check_winner()
  134.                             winner = we_get_winner(winner)
  135.                             if winner != '-':
  136.                                 pygame.display.set_caption(f"{winner}")
  137.                                 pygame.time.wait(2000)
  138.                                 pygame.quit()
  139.                                 return
  140.  
  141.  
  142.     def print_board(self):
  143.         symbols = {2: 'O', 0: ' ', 1: 'X'}
  144.         print(f'==========\n')
  145.         for row in self.board[:-1]:
  146.             print(" | ".join([symbols[symbol] for symbol in row]))
  147.             print("-" * 9)
  148.         print(" | ".join([symbols[symbol] for symbol in self.board[-1]]))
  149.         print(f'\n')
  150.  
  151.     def available_moves(self):
  152.         return [(i, j) for i in range(self.board_size) for j in range(self.board_size) if self.board[i][j] == 0]
  153.  
  154.     def make_move(self, move):
  155.         self.board[move[0]][move[1]] = self.current_player
  156.         if self.current_player == 1:
  157.             self.current_player = 2
  158.         else:
  159.             self.current_player = 1
  160.  
  161.     def check_winner(self):
  162.        
  163.         # Перевірка рядків і стовпців
  164.         for row in self.board:
  165.             if np.all(row == 1):
  166.                 return 1
  167.             elif np.all(row == 2):
  168.                 return 2
  169.  
  170.         for col in range(self.board_size):
  171.             if np.all(self.board[:, col] == 1):
  172.                 return 1
  173.             elif np.all(self.board[:, col] == 2):
  174.                 return 2
  175.  
  176.         if np.all(np.diag(self.board) == 1):
  177.             return 1
  178.         elif np.all(np.diag(self.board) == 2):
  179.             return 2
  180.  
  181.         # Перевірка діагоналей
  182.         if np.all(np.diag(np.fliplr(self.board)) == 1):
  183.             return 1
  184.         elif np.all(np.diag(np.fliplr(self.board)) == 2):
  185.             return 2
  186.        
  187.         # Нічия
  188.         if len(self.available_moves()) == 0:
  189.             return 0
  190.        
  191.         # Немає переможця
  192.         return -1
  193.    
  194.    
  195.     def train(self, agent1, agent2, epochs=1000, use_low_eps=False):
  196.        
  197.         agent1_len_Q, agent2_len_Q = [], []
  198.         arr = [round(0.1*i*epochs) for i in range(1, 11)]
  199.         x_win, o_win, draw = 0, 0, 0
  200.         total_x_win, total_o_win, total_draw = 0, 0, 0
  201.         total_x_wins, total_o_wins, total_draws = [], [], []
  202.        
  203.         for epoch in range(1, epochs+1):
  204.             if epoch in arr:
  205.                 if use_low_eps == True:
  206.                     agent1.epsilon -= agent1.epsilon*0.175
  207.                     agent2.epsilon -= agent2.epsilon*0.175
  208.                 print(f'\nAfter {epoch} learning games:\nX: {round(x_win/(arr[0])*100, 2)}% ; O: {round(o_win/arr[0]*100, 2)}% ; Draw: {round(draw/arr[0]*100, 2)}%')
  209.                 x_win, o_win, draw = 0, 0, 0
  210.            
  211.             states_agent1, states_agent2 = [], []
  212.             agent1_actions, agent2_actions = [], []
  213.            
  214.             self.__init__(board_size=self.board_size)  # Скидання ігрового поля
  215.             state = tuple(map(tuple, self.board))
  216.             agent1_action = agent1.choose_move(self)
  217.            
  218.             states_agent1.append(state)
  219.             agent1_actions.append(agent1_action)
  220.            
  221.             while True:
  222.                 self.make_move(agent1_action)
  223.                 state = tuple(map(tuple, self.board))
  224.                
  225.                 rewards = np.zeros(2)
  226.  
  227.                 winner = self.check_winner()
  228.                 if winner == 1:
  229.                     rewards[0] = 1
  230.                     rewards[1] = -1
  231.                     x_win += 1
  232.                     total_x_win += 1
  233.                 elif winner == 2:
  234.                     rewards[0] = -1
  235.                     rewards[1] = 1
  236.                     o_win += 1
  237.                     total_o_win += 1
  238.                 elif winner == 0:
  239.                     rewards[0] = 0.5
  240.                     rewards[1] = 0.5
  241.                     draw += 1
  242.                     total_draw += 1
  243.  
  244.                
  245.                 if winner != -1:
  246.                     total_x_wins.append(total_x_win)
  247.                     total_o_wins.append(total_o_win)
  248.                     total_draws.append(total_draw)
  249.                     agent1.update_Q_values(states_agent1, agent1_actions, rewards[0])
  250.                     agent2.update_Q_values(states_agent2, agent2_actions, rewards[1])
  251.                     break
  252.                
  253.                 states_agent2.append(state)
  254.                
  255.                 # Гравець 2 робить хід
  256.                 agent2_action = agent2.choose_move(self)
  257.                 agent2_actions.append(agent2_action)
  258.                 self.make_move(agent2_action)
  259.                
  260.                 state = tuple(map(tuple, self.board))
  261.                
  262.                 rewards = np.zeros(2)
  263.  
  264.                 winner = self.check_winner()
  265.                 if winner == 1:
  266.                     rewards[0] = 1
  267.                     rewards[1] = -1
  268.                     x_win += 1
  269.                     total_x_win += 1
  270.                 elif winner == 2:
  271.                     rewards[0] = -1
  272.                     rewards[1] = 1
  273.                     o_win += 1
  274.                     total_o_win += 1
  275.                 elif winner == 0:
  276.                     rewards[0] = 0.5
  277.                     rewards[1] = 0.5
  278.                     draw += 1
  279.                     total_draw += 1
  280.                
  281.                
  282.                 if winner != -1:
  283.                     total_x_wins.append(total_x_win)
  284.                     total_o_wins.append(total_o_win)
  285.                     total_draws.append(total_draw)
  286.                     agent1.update_Q_values(states_agent1, agent1_actions, rewards[0])
  287.                     agent2.update_Q_values(states_agent2, agent2_actions, rewards[1])
  288.                     break
  289.                
  290.                 states_agent1.append(state)
  291.                
  292.                 agent1_action = agent1.choose_move(self)
  293.                 agent1_actions.append(agent1_action)
  294.                
  295.             agent1_len_Q.append(len(agent1.Q_values))
  296.             agent2_len_Q.append(len(agent2.Q_values))
  297.            
  298.         return np.arange(1, epochs+1), agent1_len_Q, agent2_len_Q, [total_x_wins, total_o_wins, total_draws]
  299.    
  300.     def play(self, agent1, agent2):
  301.        
  302.         self.__init__()
  303.         agent1.epsilon = 0
  304.         agent2.epsilon = 0
  305.         agent = [agent1, agent2]
  306.         self.print_board()
  307.         while True:
  308.             winner = self.check_winner()
  309.             if winner == 1:
  310.                 print("Переміг хрестик!")
  311.                 break
  312.             elif winner == 2:
  313.                 print("Переміг нулик!")
  314.                 break
  315.             elif winner == 0:
  316.                 print("Нічия!")
  317.                 break
  318.  
  319.             move = agent[self.current_player-1].choose_move(env)
  320.             self.make_move(move)
  321.             self.print_board()
  322.            
  323.  
  324.    
  325. class SARSA_Agent:
  326.     def __init__(self, epsilon=0.3, alpha=10**-4, gamma=0.95):
  327.         self.epsilon = epsilon
  328.         self.alpha = alpha
  329.         self.gamma = gamma
  330.         self.Q_values = {}
  331.  
  332.     def choose_move(self, env):
  333.         if np.random.rand() < self.epsilon:
  334.             # Дослідження: випадковий вибір ходу
  335.             move = env.available_moves()
  336.             move = move[np.random.randint(0, len(move))]
  337.             return move
  338.            
  339.         else:
  340.             # Експлуатація: вибір ходу на основі оцінок Q-значень
  341.             state = tuple(map(tuple, env.board))
  342.             if state not in self.Q_values:
  343.                 # Ініціалізація оцінок Q-значень для нового стану
  344.                 self.Q_values[state] = np.zeros_like(env.board, dtype=float)
  345.  
  346.             # Вибір ходу з максимальним Q-значенням
  347.             Q_values_state = self.Q_values[state]
  348.            
  349.             valid_moves = env.available_moves()
  350.            
  351.             move_values = [Q_values_state[move] for move in valid_moves]
  352.  
  353.             best_moves = [move for move, value in zip(valid_moves, move_values) if value == max(move_values)]
  354.             move = random.choice(best_moves)
  355.  
  356.             return move
  357.  
  358.     def update_Q_values(self, states, actions, reward):
  359.         for state in states:
  360.             if state not in self.Q_values:
  361.                 self.Q_values[state] = np.zeros_like(state, dtype=float)
  362.  
  363.         # Обчислення нового Q-значення
  364.         td_target = reward + self.gamma * self.Q_values[states[-1]][actions[-1]]
  365.         td_error = td_target - self.Q_values[states[-2]][actions[-2]]
  366.  
  367.  
  368.         for state, action in zip(states, actions):
  369.             val = self.Q_values[state][action]
  370.             self.Q_values[state][action] += self.alpha * td_error
  371.  
  372.            
  373. class Player:
  374.     def __init__(self, epsilon=0):
  375.         self.epsilon = epsilon
  376.        
  377.     def choose_move(self, env):
  378.         valid_moves = env.available_moves()
  379.         move = input("Рядок, колонка: ")
  380.         move = [tuple(int(x)-1 for x in move.split())][0]
  381.         while move not in valid_moves:
  382.             move = input("Вибраний вами хід неможливий\nРядок, колонка: ")
  383.             move = [tuple(int(x)-1 for x in move.split())][0]
  384.        
  385.         return move
  386.    
  387.    
  388.    
  389.    
  390.    
  391. env = TicTacToe()
  392. sarsa_agent_1 = SARSA_Agent(epsilon=0.5)
  393. sarsa_agent_2 = SARSA_Agent(epsilon=0.5)
  394. ep = 100000
  395. arange, ag1_Q, ag2_Q, wins = env.train(sarsa_agent_1, sarsa_agent_2, epochs=ep, use_low_eps=True)
  396.  
  397.  
  398.  
  399. player = Player()
  400. # env.exe_play(agents=[sarsa_agent_1, sarsa_agent_2], vs='player')
  401. # env.exe_play(agents=[sarsa_agent_1, sarsa_agent_2], vs='agent', player_side='X')
  402. # env.exe_play(agents=[sarsa_agent_1, sarsa_agent_2], vs='agent', player_side='O')
  403. env.exe_play(agents=[sarsa_agent_1, sarsa_agent_2], vs='agent', player_side='random')
  404.  
  405. plt.figure(figsize=(15, 6))
  406. plt.title(f'Залежність досвіду від кількості епізодів навчання')
  407. plt.plot(arange, ag1_Q, label='Agent X')
  408. plt.plot(arange, ag2_Q, label='Agent O')
  409. plt.xlabel(f'Номер епізоду')
  410. plt.ylabel(f'Кількість станів')
  411. plt.grid()
  412. plt.legend()
  413. plt.show()
  414.  
  415. plt.figure(figsize=(15, 6))
  416. plt.title(f'Залежність перемог від кількості епізодів навчання')
  417. winner = ['X', 'O', 'Draw']
  418. for i in range(len(wins)):
  419.     plt.plot(arange, np.array(wins[i])/ep*100, label=f'{winner[i]}')
  420. plt.xlabel(f'Номер епізоду')
  421. plt.ylabel(f'% Кількість перемог')
  422. plt.grid()
  423. plt.legend()
  424. plt.show()
Add Comment
Please, Sign In to add comment