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

ИИ построен на аккуратном питоне. Входными данными является игровое поле в виде списка полей (0: незанятые, 1: player1, 2: player2). AI производит 9 выходов, и в качестве поля выбирается индекс максимума (выхода). Он тренируется против бота, который выбирает поля случайным образом. Если поле для перехода уже занято, первое свободное поле устанавливается как поле для перехода.

import neat
import pygame
import os
import json
import random
import checkpoint
import winsound
import time
import matplotlib.pyplot as plt

pygame.init()
cloud = checkpoint.Checkpointer()
p = None
GEN = 0
screen = None
pygame.display.set_caption("Tic Tac Toe")
o_icon = pygame.image.load("o.png")
x_icon = pygame.image.load("x.png")

class Field:
    def __init__(self):
        self.field = [0,0,0, 0,0,0, 0,0,0]
        self.winner = None

    def reset(self):
        self.field = [0,0,0, 0,0,0, 0,0,0]
        self.winner = None

    def display(self):
        screen.fill([0,0,0])
        self.display_pieces()
        self.display_board()
        pygame.display.update()

    def display_pieces(self):
        pos_to_pix = {
            0: (0,0),
            1: (100,0),
            2: (200,0),
            3: (0,100),
            4: (100,100),
            5: (200,100),
            6: (0, 200),
            7: (100, 200),
            8: (200, 200)
        }
        for pos in range(len(self.field)):
            if self.field[pos] == 1:
                screen.blit(x_icon, pos_to_pix[pos])
            elif self.field[pos] == 2:
                screen.blit(o_icon, pos_to_pix[pos])

    @staticmethod
    def pos_to_field(pos):
        x = pos[0]
        y = pos[1]
        if x < 100:
            x = 0
        elif x < 200:
            x = 1
        else:
            x = 2
        if y < 100:
            y = 0
        elif y < 200:
            y = 1
        else:
            y = 2
        return x+y*3

    @staticmethod
    def display_board():
        pygame.draw.rect(screen, "White", [0,99, 300,2])
        pygame.draw.rect(screen, "White", [0,199, 300,2])
        pygame.draw.rect(screen, "White", [99,0, 2,300])
        pygame.draw.rect(screen, "White", [199,0, 2,300])

    def action(self, field_id, player_id):
        self.field[field_id] = player_id

    def field_is_free(self, field):
        if self.field[field] == 0:
            return True
        return False

    def check_winner(self):
        for line in range(3):
            # check horizontal
            if self.field[line*3] == self.field[line*3+1] == self.field[line*3+2] != 0:
                self.winner = self.field[line*3]
                return True, self.winner
            if self.field[line] == self.field[line+3] == self.field[line+6] != 0:
                self.winner = self.field[line]
                return True, self.winner
        if self.field[0] == self.field[4] == self.field[8] != 0:
            self.winner = self.field[0]
            return True, self.winner
        if self.field[2] == self.field[4] == self.field[6] != 0:
            self.winner = self.field[2]
            return True, self.winner
        return False

    def first_free(self):
        for index, value in enumerate(self.field):
            if value == 0:
                return index

class Player:
    def __init__(self, id, being, brain=None):
        self.id = id
        self.being = being
        self.brain = brain

class Graph:
    def __init__(self):
        self.gens = []
        self.maxs = []
        self.losts = []
        self.drawns = []
        self.wons = []
        self.average = []
        self.last_gens = 50
        self.vmax = None

    def add(self, gen, vmax_round, lost, drawn, won):
        self.gens.append(gen)
        self.maxs.append(vmax_round)
        self.losts.append(lost)
        self.drawns.append(drawn)
        self.wons.append(won)
        self.average.append(sum(self.maxs[-min(self.last_gens,gen):]) / min(self.last_gens,gen))
        self.vmax = max(self.maxs)

    def display(self):
        plt.figure(figsize=(10,6))
        plt.plot(self.gens, self.maxs, label="Best Fitness")
        plt.plot(self.gens, self.average, label="Average Fitness last " + str(self.last_gens) + " generations")
        plt.plot(self.gens, self.losts,  label="Lost")
        plt.plot(self.gens, self.drawns,  label="Drawn")
        plt.plot(self.gens, self.wons,  label="Won")
        plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left")
        plt.grid("on")
        plt.tight_layout()
        plt.show()

    def save(self):
        with open('gens.txt', 'w') as filehandle:
            json.dump(self.gens, filehandle)
        with open('maxs.txt', 'w') as filehandle:
            json.dump(self.maxs, filehandle)
        with open('average.txt', 'w') as filehandle:
            json.dump(self.average, filehandle)
        with open('losts.txt', 'w') as filehandle:
            json.dump(self.losts, filehandle)
        with open('drawns.txt', 'w') as filehandle:
            json.dump(self.drawns, filehandle)
        with open('wons.txt', 'w') as filehandle:
            json.dump(self.wons, filehandle)

    def load(self):
        with open('gens.txt', 'r') as filehandle:
            self.gens = json.load(filehandle)
        with open('maxs.txt', 'r') as filehandle:
            self.maxs = json.load(filehandle)
        with open('average.txt', 'r') as filehandle:
            self.average = json.load(filehandle)
        with open('losts.txt', 'r') as filehandle:
            self.losts = json.load(filehandle)
        with open('drawns.txt', 'r') as filehandle:
            self.drawns = json.load(filehandle)
        with open('wons.txt', 'r') as filehandle:
            self.wons = json.load(filehandle)

graph = Graph()
board = Field()

def place_piece(player, field):
    board.field[field] = player.id

def sound():
    winsound.Beep(440,200)

def train(genomes, config):
    global GEN
    GEN += 1
    best_fitness = 0
    players = [None, None]
    best_lost, best_drawn, best_won = 0,0,0

    for index, genome in genomes:
        index -= 1
        genome.fitness = 0
        net = neat.nn.FeedForwardNetwork.create(genome, config)

        players[0] = Player(1, "random")
        players[1] = Player(2, "ai")
        lost, drawn, won = 0,0,0

        for round in range(100):
            if round == 50:
                players[0] = Player(1, "ai")
                players[1] = Player(2, "random")

            field_to_go = -1
            placed = 0
            player = players[0]

            while True:
                if player.being == "random":
                    field_to_go = random.randint(0,8)
                elif player.being == "ai":
                    output = net.activate(board.field)
                    field_to_go = output.index(max(output))
                elif player.being == "human":
                    for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                            pygame.quit()
                            break
                        elif event.type == pygame.MOUSEBUTTONUP:
                            field_to_go = board.pos_to_field(pygame.mouse.get_pos())

                if field_to_go > -1:
                    if board.field_is_free(field_to_go):
                        board.action(field_to_go, player.id)
                    else:
                        board.action(board.first_free(), player.id)

                    if player == players[0]:
                        player = players[1]
                    else:
                        player = players[0]
                    placed += 1

                if shown:
                    board.display()
                    time.sleep(1)

                if board.check_winner() or placed == 9:
                    if board.winner is None:
                        genome.fitness += 1
                        drawn += 1
                    elif players[board.winner-1].being == "ai":
                        genome.fitness += 2
                        won += 1
                    else:
                        genome.fitness -= 0
                        lost += 1
                    board.reset()
                    break
        if genome.fitness > best_fitness:
            best_fitness = genome.fitness
            best_lost = lost
            best_drawn = drawn
            best_won = won
    graph.add(GEN, best_fitness, best_lost, best_drawn, best_won)

def test(genome):
    result = [0,0,0]
    players = [None, None]
    for round in range(1000):
        placed = 0
        if round % 2 == 0:
            players[0] = Player(1, "ai")
            players[1] = Player(2, "human")
        else:
            players[0] = Player(1, "human")
            players[1] = Player(2, "ai")

        player = players[0]
        while True:
            field_to_go = -1
            if player.being == "random":
                field_to_go = random.randint(0,8)
            elif player.being == "ai":
                output = genome.activate(board.field)
                field_to_go = output.index(max(output))
            elif player.being == "human":
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        break
                    elif event.type == pygame.MOUSEBUTTONUP:
                        field_to_go = board.pos_to_field(pygame.mouse.get_pos())

            if field_to_go > -1:
                if board.field_is_free(field_to_go):
                    board.action(field_to_go, player.id)
                else:
                    board.action(board.first_free(), player.id)
                if player == players[0]:
                    player = players[1]
                else:
                    player = players[0]
                placed += 1

            if shown:
                pygame.event.get()
                board.display()
                time.sleep(0.75)

            if board.check_winner() or placed == 9:
                if board.winner is None:
                    result[1] += 1
                elif players[board.winner-1].being == "ai":
                    result[2] += 1
                else:
                    result[0] += 1
                if shown:
                    if board.winner is None:
                        print("None")
                    else:
                        print(players[board.winner-1].being + " " + str(player.id))
                board.reset()
                break
    print(result)

def run(config_path):
    global shown
    global GEN
    global screen
    config = neat.config.Config(neat.DefaultGenome, neat.DefaultReproduction,
                                neat.DefaultSpeciesSet, neat.DefaultStagnation, config_path)
    nc_name = "population"

    testing = False
    shown = False
    new = True

    if testing:
        if shown:
            screen = pygame.display.set_mode((300, 300))
            pygame.display.set_caption("Tic Tac Toe")
        test(neat.nn.FeedForwardNetwork.create(cloud.restore_checkpoint("population").population.best_genome, config))
        quit()

    if new:
        p = neat.Population(config)
        p.add_reporter(neat.StdOutReporter(True))
        p.add_reporter(neat.StatisticsReporter())
    else:
        p = cloud.restore_checkpoint(nc_name).population
        graph.load()
    GEN = p.generation
    p.run(train,250)
    print(time.strftime("%H:%M:%S", time.localtime()))
    cloud.save_checkpoint(config, p, p.species, GEN, nc_name)
    graph.save()
    graph.display()
    sound()

if __name__ == "__main__":
    local_dir = os.path.dirname(__file__)
    config_path = os.path.join(local_dir, "config_feedforward")
    run(config_path)

После 100 игр ИИ относительно быстро набирает 175 очков. Но с этого момента он не улучшается. И все равно проигрывает в играх, чего не бывает, когда вы играете идеально. Что я делаю неправильно? Спасибо за вашу помощь 🙂

введите описание изображения здесь

0

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

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