Ich habe gehört, dass Chainer RL Beta veröffentlicht wurde, also habe ich es sofort verwendet. Lesen Sie hier die Quelle von Kurzanleitung und ändern Sie sie für die dritte Zeile (○ × Spiel). Ich bin.
Installieren Sie zuerst ChainerRL.
pip install chainerrl
cmake ist erforderlich. Wenn Sie es nicht installiert haben, installieren Sie es bitte im Voraus.
brew install cmake
Meine Umgebung ist wie folgt.
Unabhängig von der Art des Spielers (DQN, zufälliger Treffer, Mensch usw.) benötigen Sie ein Spielbrett, um das ○ × -Spiel zu spielen. Erstellen Sie es also zuerst. Dieses Mal schreibe ich alle Quellen in eine Datei, ohne die Datei zu teilen. Importieren Sie daher zu Beginn die erforderlichen Bibliotheken.
dqn.py
import chainer
import chainer.functions as F
import chainer.links as L
import chainerrl
import numpy as np
#Spielbrett
class Board():
def reset(self):
self.board = np.array([0] * 9, dtype=np.float32)
self.winner = None
self.missed = False
self.done = False
def move(self, act, turn):
if self.board[act] == 0:
self.board[act] = turn
self.check_winner()
else:
self.winner = turn*-1
self.missed = True
self.done = True
def check_winner(self):
win_conditions = ((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 cond in win_conditions:
if self.board[cond[0]] == self.board[cond[1]] == self.board[cond[2]]:
if self.board[cond[0]]!=0:
self.winner=self.board[cond[0]]
self.done = True
return
if np.count_nonzero(self.board) == 9:
self.winner = 0
self.done = True
def get_empty_pos(self):
empties = np.where(self.board==0)[0]
if len(empties) > 0:
return np.random.choice(empties)
else:
return 0
def show(self):
row = " {} | {} | {} "
hr = "\n-----------\n"
tempboard = []
for i in self.board:
if i == 1:
tempboard.append("○")
elif i == -1:
tempboard.append("×")
else:
tempboard.append(" ")
print((row + hr + row + hr + row).format(*tempboard))
Die folgenden fünf Funktionen. Es tut mir leid für die unklare Quelle für Python-Anfänger, aber Sie können sehen, was sie tun.
--reset Initialisiert das Spielbrett. Laufen Sie vor dem Beginn jeder Episode --move Handplatzierung durchführen. Beurteilung von Sieg oder Niederlage, Fehler (Platzierung auf einem Feld, das nicht platziert werden kann) und Spielende nach Platzierung --check_winner Siegesurteil --get_empty_pos Ruft einen der Indizes der Zellen ab, die zufällig platziert werden können. Wie später beschrieben wird, wird es bei zufälligen Treffern verwendet. --show Zeigt den Kartenstatus an. Zum Spielen gegen Menschen
Es scheint gut zu sein, gelegentlich ein Abenteuer zu machen, um nicht in eine lokale Lösung zu verfallen, und Quickstart hat eine solche Implementierung, daher folgt dies auch hier. Ich habe das Fitnessstudio in Quickstart verwendet, aber hier habe ich keine andere Wahl, als es selbst zu machen. Fügen Sie am Ende den folgenden Code hinzu.
dqn.py
#Zufälliges Funktionsobjekt für den Explorer
class RandomActor:
def __init__(self, board):
self.board = board
self.random_count = 0
def random_action_func(self):
self.random_count += 1
return self.board.get_empty_pos()
random_action_func ist der Schlüssel zu diesem Objekt. Rufen Sie get_empty_pos des zuvor erstellten Boards auf, um den Platz zu erhalten, der platziert werden kann, und geben Sie ihn an den Anrufer zurück. Außerdem wird der Zähler erhöht, sodass Sie später sehen können, wie oft diese Funktion als statistische Information verwendet wurde (ob DQN sie zufällig zurückgegeben hat, anstatt nachzudenken). Warum haben Sie sich die Mühe gemacht, so etwas zu einem separaten Objekt zu machen? Wird später erklärt.
Dies ist der Hauptpunkt für DQN, und Chainer RL kommt ins Spiel.
dqn.py
#Q-Funktion
class QFunction(chainer.Chain):
def __init__(self, obs_size, n_actions, n_hidden_channels=81):
super().__init__(
l0=L.Linear(obs_size, n_hidden_channels),
l1=L.Linear(n_hidden_channels, n_hidden_channels),
l2=L.Linear(n_hidden_channels, n_hidden_channels),
l3=L.Linear(n_hidden_channels, n_actions))
def __call__(self, x, test=False):
#-Weil es sich um 1 handelt, undicht_relu
h = F.leaky_relu(self.l0(x))
h = F.leaky_relu(self.l1(h))
h = F.leaky_relu(self.l2(h))
return chainerrl.action_value.DiscreteActionValue(self.l3(h))
···das ist alles. Es ist einfach genug, um ein wenig zu schlagen. Es ist fast das gleiche wie das normale Definieren eines NN.
Jetzt, da wir etwas zu bauen haben, müssen wir nur noch die Umgebung und den Agenten vorbereiten und den Fortschritt des Spiels aufbauen. Zuerst aus der Umgebung und Agent.
dqn.py
#Board Vorbereitung
b = Board()
#Vorbereiten eines zufälligen Funktionsobjekts für den Explorer
ra = RandomActor(b)
#Anzahl der Dimensionen von Umgebung und Verhalten
obs_size = 9
n_actions = 9
# Q-Funktions- und Optimierungssetup
q_func = QFunction(obs_size, n_actions)
optimizer = chainer.optimizers.Adam(eps=1e-2)
optimizer.setup(q_func)
#Abzinsungssatz der Belohnung
gamma = 0.95
# Epsilon-Gelegentlich Abenteuer mit Gier. Beenden Sie in 50000 Schritten_Epsilon werden
explorer = chainerrl.explorers.LinearDecayEpsilonGreedy(
start_epsilon=1.0, end_epsilon=0.3, decay_steps=50000, random_action_func=ra.random_action_func)
#Puffer, der in der in DQN verwendeten Lernmethode namens Experience Replay verwendet wird
replay_buffer = chainerrl.replay_buffer.ReplayBuffer(capacity=10 ** 6)
#Agentengenerierung (Wiedergabe)_Zwei Sharing Buffer etc.)
agent_p1 = chainerrl.agents.DoubleDQN(
q_func, optimizer, replay_buffer, gamma, explorer,
replay_start_size=500, update_frequency=1,
target_update_frequency=100)
agent_p2 = chainerrl.agents.DoubleDQN(
q_func, optimizer, replay_buffer, gamma, explorer,
replay_start_size=500, update_frequency=1,
target_update_frequency=100)
Übrigens, bei Epsilon-gierig wird der zufällige Schauspieler erscheinen, den ich zuvor gemacht habe. Es ist notwendig, einen Verweis auf die Funktion zu übergeben, die verwendet werden soll, wenn Sie im Voraus an den Explorer gehen, aber es scheint, dass Sie dieser Funktion kein Argument übergeben können? Also habe ich einen Verweis auf das Spielbrett an die Mitgliedsvariable des RandomActor-Objekts übergeben, die im Voraus instanziiert wurde, und bei der internen Verarbeitung des Explorers habe ich random_action_func gebeten, ohne Argument aufgerufen zu werden, und es in Ordnung gebracht. Ich denke, es gibt einen intelligenteren Weg, also lassen Sie es mich bitte wissen. ..
Darüber hinaus wurde die Methode von ε-gierig in eine Methode (LinearDecayEpsilonGreedy) geändert, die den Wert schrittweise reduziert, anstatt ihn zu einem konstanten Wert zu machen. Beginnen Sie mit 1,0 = immer zufällig und reduzieren Sie dann über 50.000 Schritte auf 0,3. Ich weiß nicht, ob diese Nummer auch gültig ist, daher kann es sinnvoll sein, sie auf verschiedene Arten zu ändern.
Der Agent erstellt P1 und P2, die sich einen Optimierer und einen Wiederholungspuffer teilen, um gegeneinander zu kämpfen.
Ich denke, es ist ein bisschen langweilig, weil ich es schon tun möchte, aber wenn ich das hinzufüge, kann ich es tun, also bitte haben Sie etwas Geduld.
dqn.py
#Anzahl der Lernspiele
n_episodes = 20000
#Erklärung des Zählers
miss = 0
win = 0
draw = 0
#Wiederholte Folgen
for i in range(1, n_episodes + 1):
b.reset()
reward = 0
agents = [agent_p1, agent_p2]
turn = np.random.choice([0, 1])
last_state = None
while not b.done:
#Platzierungsmassenerfassung
action = agents[turn].act_and_train(b.board.copy(), reward)
#Platzierung durchführen
b.move(action, 1)
#Als Ergebnis der Platzierung setzen Sie am Ende Werte in Belohnung und Zähler und lernen
if b.done == True:
if b.winner == 1:
reward = 1
win += 1
elif b.winner == 0:
draw += 1
else:
reward = -1
if b.missed is True:
miss += 1
#Lerne, indem du die Episode beendest
agents[turn].stop_episode_and_train(b.board.copy(), reward, True)
#Die andere Partei beendet auch die Episode und lernt. Lerne die Fehler deines Gegners nicht als Gewinn
if agents[1 if turn == 0 else 0].last_state is not None and b.missed is False:
#Zuletzt in der vorherigen Runde gespeichert_Übergeben Sie den Status als Status, nachdem Sie die Aktion ausgeführt haben
agents[1 if turn == 0 else 0].stop_episode_and_train(last_state, reward*-1, True)
else:
#Speichern Sie den letzten Status der Runde zum Lernen
last_state = b.board.copy()
#Invertieren Sie den Wert auf der Karte, wenn Sie fortfahren
b.board = b.board * -1
#Schalter dreht
turn = 1 if turn == 0 else 0
#Fortschrittsanzeige auf der Konsole
if i % 100 == 0:
print("episode:", i, " / rnd:", ra.random_count, " / miss:", miss, " / win:", win, " / draw:", draw, " / statistics:", agent_p1.get_statistics(), " / epsilon:", agent_p1.explorer.epsilon)
#Zählerinitialisierung
miss = 0
win = 0
draw = 0
ra.random_count = 0
if i % 10000 == 0:
#Modell für jeweils 10000 Folgen speichern
agent_p1.save("result_" + str(i))
print("Training finished.")
20000 Besteht aus einer verschachtelten for-Anweisung, die das Spiel wiederholt, und einer while-Anweisung, die die Spielrunde wiederholt. Als Punkt sind sowohl der erste als auch der zweite Angriff der Agent selbst. In diesem Spiel platzieren Sie anstelle von ○ × Ihre eigene Hand als 1 und die Hand Ihres Gegners als -1. Da Sie jedoch die Umgebung und die Aktionen des ersten und zweiten Angriffs lernen möchten, Setzen Sie immer eine 1 auf die Tafel, anstatt die Zeichen zu teilen.
#Platzierung durchführen
b.move(action, 1)
Wenn es so bleibt, wie es ist, ist das Board natürlich voll mit 1, so dass der Code des Boards umgekehrt wird, wenn der Zug geändert wird.
#Invertieren Sie den Wert auf der Karte, wenn Sie fortfahren
else:
b.board = b.board * -1
Und schließlich speichern wir das trainierte Modell. ChainerRL scheint dieses Verzeichnis zu erstellen, auch wenn es nicht vorhanden ist. Daher habe ich versucht, den Verlauf im Verzeichnis mit der Anzahl der Folgen am Ende für jeweils 10.000 Folgen zu speichern. Da wir mit der gleichen Erfahrung trainieren, speichern wir nur agent_p1.
Lass es uns jetzt machen ...! Da der Wert von epsilon am Anfang groß ist, handelt es sich bei den meisten um zufällige Treffer (die Häufigkeit, mit der der rnd-Wert zufällig getroffen wird). Daher gibt es nur wenige Fehler, aber wenn die Anzahl der zufälligen Treffer allmählich abnimmt, steigt die Wahrscheinlichkeit, mit den Händen zu schlagen, von denen DQN glaubt, dass sie vorübergehend zunehmen. Mit fortschreitendem Lernen steigt sie jedoch auch auf das 15.000-fache an. Als es überschritten wurde, wurde es fast die erste Hälfte der einstelligen Zahl.
episode: 100 / rnd: 761 / miss: 1 / win: 85 / draw: 14 / statistics: [('average_q', 0.11951273068342624), ('average_loss', 0.09235552993858538)] / epsilon: 0.994778
episode: 200 / rnd: 722 / miss: 3 / win: 85 / draw: 12 / statistics: [('average_q', 0.35500590929140996), ('average_loss', 0.12790488153218765)] / epsilon: 0.9895
episode: 300 / rnd: 756 / miss: 6 / win: 82 / draw: 12 / statistics: [('average_q', 0.6269444783473722), ('average_loss', 0.12164947750267516)] / epsilon: 0.984278
: (Ausgelassen)
episode: 19800 / rnd: 212 / miss: 1 / win: 69 / draw: 30 / statistics: [('average_q', 0.49387913595157096), ('average_loss', 0.07891365175610675)] / epsilon: 0.3
episode: 19900 / rnd: 229 / miss: 1 / win: 61 / draw: 38 / statistics: [('average_q', 0.49195677296191365), ('average_loss', 0.07796313042393459)] / epsilon: 0.3
episode: 20000 / rnd: 216 / miss: 0 / win: 70 / draw: 30 / statistics: [('average_q', 0.509864846571749), ('average_loss', 0.07866546801090374)] / epsilon: 0.3
Training finished.
Es scheint, dass Sie schlagen, ohne Fehler zu machen, und obwohl Sie gelegentlich zufällig schlagen, wird es ein ziemliches Unentschieden sein, also werde ich gegen mich selbst spielen, um die Stärke zu überprüfen.
Erstellen Sie zunächst ein Objekt namens HumanPlayer als Schnittstelle, auf die Menschen treffen können.
dqn.py
#Menschlicher Spieler
class HumanPlayer:
def act(self, board):
valid = False
while not valid:
try:
act = input("Please enter 1-9: ")
act = int(act)
if act >= 1 and act <= 9 and board[act-1] == 0:
valid = True
return act-1
else:
print ("Invalid move")
except Exception as e:
print (act + " is invalid")
Dies ist der Fortschrittsteil. Während der DQN-Agent auf 1 und der Mensch auf -1 festgelegt wird, entscheiden der erste und der zweite Angriff vor Beginn der Episode, ob der DQN-Agent der erste Angriff ist, und steuern, ob das erste Mal übersprungen werden soll oder nicht. Ich bin. In diesem Zusammenhang sind Agenten immer ○ und Menschen sind immer ×, unabhängig davon, ob sie erste oder zweite sind.
dqn.py
#Überprüfung
human_player = HumanPlayer()
for i in range(10):
b.reset()
dqn_first = np.random.choice([True, False])
while not b.done:
#DQN
if dqn_first or np.count_nonzero(b.board) > 0:
b.show()
action = agent_p1.act(b.board.copy())
b.move(action, 1)
if b.done == True:
if b.winner == 1:
print("DQN Win")
elif b.winner == 0:
print("Draw")
else:
print("DQN Missed")
agent_p1.stop_episode()
continue
#Mensch
b.show()
action = human_player.act(b.board.copy())
b.move(action, -1)
if b.done == True:
if b.winner == -1:
print("HUMAN Win")
elif b.winner == 0:
print("Draw")
agent_p1.stop_episode()
print("Test finished.")
Der Punkt ist, dass der Agent hier nicht lernt, also sollte er act () und stop_episode () verwenden. Dies ist auch als Schnellstart.
Jetzt, da wir für das Match bereit sind, ist es unfruchtbar, wieder 20.000 Mal zu trainieren. Laden Sie also den gespeicherten Agenten. Eigentlich ist es klug, zwischen dem Lernen mit dem Ausführungsparameter dqn.py / Laden eines vorhandenen Modells zu wechseln. Da ich jedoch schneller spielen möchte, überspringe ich den Lernprozess, indem ich die Anzahl der Lernepisoden wie folgt auf 0 setze.
dqn.py
#Anzahl der Lernspiele
n_episodes = 0
Fügen Sie nach Abschluss des Schulungsprozesses den folgenden Code hinzu, um das Modell zu laden.
dqn.py
print("Training finished.")
agent_p1.load("result_20000") #← Fügen Sie dies hinzu
Wenn Sie bereit sind, ist es Zeit zu spielen!
Training finished.
| |
-----------
| |
-----------
| |
| |
-----------
| |
-----------
○ | |
Please enter 1-9: 1
× | |
-----------
| |
-----------
○ | |
× | |
-----------
| |
-----------
○ | | ○
Please enter 1-9: 8
Du kannst spielen! Yay! !!
Vielen Dank, dass Sie mich mit DQN und Python vertraut gemacht haben. Ich bin sehr froh, dass er so weit gewachsen ist, dass er mit ziemlicher Sicherheit den Stein treffen kann, ohne die Regeln zu lehren. Darüber hinaus ist es viel sauberer als die Implementierung von DQN mit Chainer. ChainerRL ist unglaublich! !! Mit einem besseren Ausblick scheint es möglich zu sein, das Mischen von Fehlern zu verhindern, um verschiedene Dinge zu verbessern.
Ich denke, es gibt viele Dinge, die falsch sind, wie "das sollte getan werden", "das Lernen schreitet auf diese Weise voran" und "ich kann damit nicht lernen", also würde ich es begrüßen, wenn Sie auf verschiedene Dinge hinweisen könnten. Danke für Ihre Kooperation.
Was mich besonders beunruhigt, ist, dass die Art und Weise, wie der Agent getroffen wird, fast jedes Mal dieselbe zu sein scheint. Muss ich es abenteuerlicher machen? Wenn Sie 350.000 Episoden trainieren, wird es wie gewohnt getroffen, so dass es stark ist, und wenn Sie ε auf 0 setzen, wird es fast jedes Mal gezogen, also ist es eine gute Sache. .. Von 150.000 bis 200.000 Folgen wurden die Ergebnisse und Verluste konstant.
Vorerst werde ich die gesamte Quelle veröffentlichen. Wenn die Umgebung vollständig ist, können Sie sie kopieren, einfügen und sofort verschieben.
dqn.py
import chainer
import chainer.functions as F
import chainer.links as L
import chainerrl
import numpy as np
#Spielbrett
class Board():
def reset(self):
self.board = np.array([0] * 9, dtype=np.float32)
self.winner = None
self.missed = False
self.done = False
def move(self, act, turn):
if self.board[act] == 0:
self.board[act] = turn
self.check_winner()
else:
self.winner = turn*-1
self.missed = True
self.done = True
def check_winner(self):
win_conditions = ((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 cond in win_conditions:
if self.board[cond[0]] == self.board[cond[1]] == self.board[cond[2]]:
if self.board[cond[0]]!=0:
self.winner=self.board[cond[0]]
self.done = True
return
if np.count_nonzero(self.board) == 9:
self.winner = 0
self.done = True
def get_empty_pos(self):
empties = np.where(self.board==0)[0]
if len(empties) > 0:
return np.random.choice(empties)
else:
return 0
def show(self):
row = " {} | {} | {} "
hr = "\n-----------\n"
tempboard = []
for i in self.board:
if i == 1:
tempboard.append("○")
elif i == -1:
tempboard.append("×")
else:
tempboard.append(" ")
print((row + hr + row + hr + row).format(*tempboard))
#Zufälliges Funktionsobjekt für den Explorer
class RandomActor:
def __init__(self, board):
self.board = board
self.random_count = 0
def random_action_func(self):
self.random_count += 1
return self.board.get_empty_pos()
#Q-Funktion
class QFunction(chainer.Chain):
def __init__(self, obs_size, n_actions, n_hidden_channels=81):
super().__init__(
l0=L.Linear(obs_size, n_hidden_channels),
l1=L.Linear(n_hidden_channels, n_hidden_channels),
l2=L.Linear(n_hidden_channels, n_hidden_channels),
l3=L.Linear(n_hidden_channels, n_actions))
def __call__(self, x, test=False):
#-Weil es sich um 1 handelt, undicht_relu
h = F.leaky_relu(self.l0(x))
h = F.leaky_relu(self.l1(h))
h = F.leaky_relu(self.l2(h))
return chainerrl.action_value.DiscreteActionValue(self.l3(h))
#Board Vorbereitung
b = Board()
#Vorbereiten eines zufälligen Funktionsobjekts für den Explorer
ra = RandomActor(b)
#Anzahl der Dimensionen von Umgebung und Verhalten
obs_size = 9
n_actions = 9
# Q-Funktions- und Optimierungssetup
q_func = QFunction(obs_size, n_actions)
optimizer = chainer.optimizers.Adam(eps=1e-2)
optimizer.setup(q_func)
#Abzinsungssatz der Belohnung
gamma = 0.95
# Epsilon-Gelegentlich Abenteuer mit Gier. Beenden Sie in 50000 Schritten_Epsilon werden
explorer = chainerrl.explorers.LinearDecayEpsilonGreedy(
start_epsilon=1.0, end_epsilon=0.3, decay_steps=50000, random_action_func=ra.random_action_func)
#Puffer, der in der in DQN verwendeten Lernmethode namens Experience Replay verwendet wird
replay_buffer = chainerrl.replay_buffer.ReplayBuffer(capacity=10 ** 6)
#Agentengenerierung (Wiedergabe)_Zwei Sharing Buffer etc.)
agent_p1 = chainerrl.agents.DoubleDQN(
q_func, optimizer, replay_buffer, gamma, explorer,
replay_start_size=500, update_frequency=1,
target_update_frequency=100)
agent_p2 = chainerrl.agents.DoubleDQN(
q_func, optimizer, replay_buffer, gamma, explorer,
replay_start_size=500, update_frequency=1,
target_update_frequency=100)
#Anzahl der Lernspiele
n_episodes = 20000
#Erklärung des Zählers
miss = 0
win = 0
draw = 0
#Wiederholte Folgen
for i in range(1, n_episodes + 1):
b.reset()
reward = 0
agents = [agent_p1, agent_p2]
turn = np.random.choice([0, 1])
last_state = None
while not b.done:
#Platzierungsmassenerfassung
action = agents[turn].act_and_train(b.board.copy(), reward)
#Platzierung durchführen
b.move(action, 1)
#Als Ergebnis der Platzierung setzen Sie am Ende Werte in Belohnung und Zähler und lernen
if b.done == True:
if b.winner == 1:
reward = 1
win += 1
elif b.winner == 0:
draw += 1
else:
reward = -1
if b.missed is True:
miss += 1
#Lerne, indem du die Episode beendest
agents[turn].stop_episode_and_train(b.board.copy(), reward, True)
#Die andere Partei beendet auch die Episode und lernt. Lerne die Fehler deines Gegners nicht als Gewinn
if agents[1 if turn == 0 else 0].last_state is not None and b.missed is False:
#Zuletzt in der vorherigen Runde gespeichert_Übergeben Sie den Status als Status, nachdem Sie die Aktion ausgeführt haben
agents[1 if turn == 0 else 0].stop_episode_and_train(last_state, reward*-1, True)
else:
#Speichern Sie den letzten Status der Runde zum Lernen
last_state = b.board.copy()
#Invertieren Sie den Wert auf der Karte, wenn Sie fortfahren
b.board = b.board * -1
#Schalter dreht
turn = 1 if turn == 0 else 0
#Fortschrittsanzeige auf der Konsole
if i % 100 == 0:
print("episode:", i, " / rnd:", ra.random_count, " / miss:", miss, " / win:", win, " / draw:", draw, " / statistics:", agent_p1.get_statistics(), " / epsilon:", agent_p1.explorer.epsilon)
#Zählerinitialisierung
miss = 0
win = 0
draw = 0
ra.random_count = 0
if i % 10000 == 0:
#Modell für jeweils 10000 Folgen speichern
agent_p1.save("result_" + str(i))
print("Training finished.")
#Menschlicher Spieler
class HumanPlayer:
def act(self, board):
valid = False
while not valid:
try:
act = input("Please enter 1-9: ")
act = int(act)
if act >= 1 and act <= 9 and board[act-1] == 0:
valid = True
return act-1
else:
print("Invalid move")
except Exception as e:
print(act + " is invalid")
#Überprüfung
human_player = HumanPlayer()
for i in range(10):
b.reset()
dqn_first = np.random.choice([True, False])
while not b.done:
#DQN
if dqn_first or np.count_nonzero(b.board) > 0:
b.show()
action = agent_p1.act(b.board.copy())
b.move(action, 1)
if b.done == True:
if b.winner == 1:
print("DQN Win")
elif b.winner == 0:
print("Draw")
else:
print("DQN Missed")
agent_p1.stop_episode()
continue
#Mensch
b.show()
action = human_player.act(b.board.copy())
b.move(action, -1)
if b.done == True:
if b.winner == -1:
print("HUMAN Win")
elif b.winner == 0:
print("Draw")
agent_p1.stop_episode()
print("Test finished.")
Recommended Posts