Weiter vom letzten Mal möchte ich den Code erklären. Das ist das letzte Mal. Othello-Aus der dritten Zeile von "Implementation Deep Learning" (1) http://qiita.com/Kumapapa2012/items/cb89d73782ddda618c99 Klicken Sie hier für nachfolgende Artikel. Othello-Aus der dritten Zeile von "Implementation Deep Learning" (3) http://qiita.com/Kumapapa2012/items/3cc20a75c745dc91e826 Othello-Aus der dritten Zeile von "Implementation Deep Learning" (4) [Ende] http://qiita.com/Kumapapa2012/items/9cec4e6d2c935d11f108
Der Quellcode ist hier. https://github.com/Kumapapa2012/Learning-Machine-Learning/tree/master/Reversi
Obwohl ich es erkläre, ist dies nur ein Beispiel aus dem Buch "Implementation Deep Learning" mit einem Othello-Spiel. Aus diesem Grund werden in diesem Artikel nur die Änderungen gegenüber dem Beispiel und dem Code des erstellten Othello erläutert. Informationen zur Bedeutung und Rolle jeder Skriptdatei, zum Ablauf der Bedienung und zu einer detaillierten Erläuterung von Deep Q-Leaning finden Sie im Buch "Implementierung von Deep Learning".
Es ist ungefähr 3 Monate her, seit ich richtig studiert habe, also kann es Fehler geben. Wenn Sie Fehler, Kommentare, Fragen oder Fragen haben, würden wir uns freuen, wenn Sie dies kommentieren könnten.
Vor dem Ausführen der folgenden Skripts muss die Umgebung so eingerichtet werden, dass das Beispiel für "Implementation Deep Learning" ausgeführt werden kann. Das Skript muss nach dem Aufrufen der Anaconda-Umgebung ausgeführt werden, indem ein Befehl wie der folgende ausgeführt wird, wie im Buch "Implementation Deep Learning" beschrieben.
. activate main (Oder Quelle aktivieren Haupt)
Außerdem muss RL_Glue gestartet werden, bevor jedes Skript ausgeführt werden kann. (Ausgenommen "Game_Reversi_Test.py").
agent.py Es ist ein "Agent", der maschinelles Lernen durchführt. Usage:
python agent.py [--gpu <gpu id>] [--size <board size>] --gpu: GPU-ID (falls weggelassen oder negativer Wert, wird die CPU verwendet) --size: Othello-Boardgröße (Standardwert 6, wenn weggelassen)
Description:
Es ist ein Agent, der das Lernen von Themen mithilfe von DQN durchführt.
Die Boardgröße muss mit der Größe übereinstimmen, die beim Start in environment.py angegeben wurde. [^ 1]
Über RL_Glue erhält es den folgenden Inhalt von environment.py als eindimensionales Array. DQN ermittelt den besten Zug und gibt ihn an RL_Glue zurück.
Schicht | Inhalt |
---|---|
0 | Position Ihres Rahmens(Agent) |
1 | Position der gegnerischen Figur(Environment) |
2 | Position, an der Sie den Rahmen platzieren können |
3 | Position, an der der Gegner das Stück platzieren kann |
Der Agent platziert weiterhin Stücke, solange angegeben ist, wo sie in Schicht 2 platziert werden können. Schicht 2 Wenn es keinen Platz für Ihr Stück gibt, wird der Agent passen.
Beispiel) In den folgenden Situationen auf einer 6x6-Karte:
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | -1 | 0 | 0 | 0 |
3 | 0 | 1 | 1 | 0 | 0 | 0 |
4 | 0 | 1 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
Die Eingabeinhalte sind wie folgt
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | 0 | 0 | 0 | 0 |
3 | 0 | 1 | 1 | 0 | 0 | 0 |
4 | 0 | 1 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 | 1 | 0 | 0 |
2 | 0 | 0 | 0 | 1 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
- | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 1 | 0 | 1 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 |
Als Eingabeschicht hat das neuronale Netzwerk die gleiche Anzahl von Elementen wie die obigen Informationen, die aus der Umgebung erhalten wurden. Py x die Anzahl der Aktionen, die während des Trainings zurückverfolgt werden können.
Auch dieses Mal haben wir die Klasse QNet
init``` geändert, um 8 vollständig verbundene versteckte Ebenen zu haben.
Die Ausgabeschicht hat eine Anzahl von Knoten, die der Kartengröße entsprechen.
Von den Knoten in dieser Ausgabeschicht ist der mit dem höchsten Wert die Position des vom Agenten ausgewählten Frames.
Darüber hinaus gibt es die folgenden Unterschiede zu Büchern.
Wert | Bücher | Dieser Code | Erläuterung |
---|---|---|---|
self.n_rows | 3 | size(6 oder mehr gerade) | Brettgröße |
self.bdim | self.dim * 2 | self.dim * 4 | Größe der Trainingsdaten |
self.capacity | 1 * 10**4 | 2 * 10**4 | Speicheraufbewahrungsnummer wiedergeben |
self.n_frames | 3 | 9 | Anzahl der Aktionen, die auf das Lernen zurückgehen |
self.batch_size | 32 | 128 | Chargengröße zum Zeitpunkt des Lernens |
In Bezug auf den Implementierungsinhalt ist der Operationsablauf derselbe wie in der Erläuterung im Buch, obwohl es einige Änderungen gemäß den obigen Parameteränderungen gibt.
environment.py Es ist die "Umgebung", die der Gegner des Agenten ist. Usage:
python environment.py [--size <board size>] --size: Othello-Boardgröße (Standardwert 6, wenn weggelassen)
Description:
Aus dem Beispiel "environment.py" im Buch "Implementation Deep Learning" wird die Logik der dritten Zeile entfernt und die Logik des Spiels in "Game_Reversi.py" implementiert.
Daher wird eine Instanz von Game_Reversi wie folgt erstellt und initialisiert.
environment.py
import Game_Reversi as game_base
#(Unterlassung)
def __init__(self, size):
self.game= game_base.Game_Reversi(size,size)
#(Folgendes wird weggelassen)
Das Spielbrett ist ein Quadrat der angegebenen Größe und wird im g_board der Game_Reversi-Klasse als N x N Int-Array gespeichert. Jedes Array-Element enthält einen der folgenden Werte:
0 | Freiraum |
1 | Agent |
-1 | Umgebung |
Indem der Spieler mit einem positiven oder negativen Wert von 1 ausgedrückt wird, können Aktionen und Beurteilungen einfach durch Umkehren des Vorzeichens ausgeführt werden, und das Spiel kann mit einem Minimum an Code gespielt werden. Der in "agent.py" beschriebene Status des Spiels wird durch die Funktion "build_map_from_game" erstellt.
environment.py
def build_map_from_game(self):
map_data=[]
#0: Aktuelle Karte(Agent=Position von 1 Rahmen)
board_data=(self.game.g_board.reshape(-1)== self.game.turn).astype(int)
map_data.extend(board_data)
#1: Aktuelle Karte(Environment=-Position von 1 Rahmen)
board_data=(self.game.g_board.reshape(-1)==-self.game.turn).astype(int)
map_data.extend(board_data)
#2: Ein Ort, an dem der Agent platziert werden kann."turn"Agent und Umgebung werden durch das Positive und Negative von ausgedrückt.
#Wenn positiv, wird es ein Agent.
pos_available=np.zeros_like(self.game.g_board)
l_available=self.game.getPositionAvail(self.game.turn)
for avail in l_available:
pos_available[tuple(avail)]=1
map_data.extend(pos_available.reshape(-1).tolist())
#3: Ort, an dem die Umgebung platziert werden kann
pos_available=np.zeros_like(self.game.g_board)
l_available=self.game.getPositionAvail(-self.game.turn)
for avail in l_available:
pos_available[tuple(avail)]=1
map_data.extend(pos_available.reshape(-1).tolist())
return map_data
Grundsätzlich wird der Board-Status erstellt und der Ort, an dem der Rahmen platziert werden kann, wird durch Ändern des Vorzeichens der Drehung erstellt, und ein eindimensionales Array wird erstellt. Setzen Sie zu Beginn des Spiels einfach das Spielfeld zurück und senden Sie den mit der obigen Funktion erstellten Inhalt.
environment.py
def env_start(self):
# plan:Reversi-Board-Initialisierung
self.game.resetBoard()
#Kartendaten erstellen
self.map=self.build_map_from_game()
#(Unterlassung)
#RL den Zustand der Karte_Über Kleber an den Agenten weitergeben
observation = Observation()
observation.intArray = self.map
return observation
Nach dem Start des Spiels gibt der Agent die Aktion (wo das Stück platziert werden soll) an, um die folgenden Aktionen auszuführen.
Agentenaktionen sind ganzzahlige Werte. Für eine 6x6-Karte ist es eine ganze Zahl von -1 ≤ a ≤ 35. -1 ist der Pfad. Verwenden Sie die Brettgröße, um dies in ein Zeilen- und Spaltentupel auf dem Brett umzuwandeln, und übergeben Sie es an die Schrittmethode von Game_Reversi, um die Aktion auszuführen.
environment.py
if int_action_agent==-1 :
step_raw_col=(-1,-1)
else :
step_raw_col=(int_action_agent//self.n_cols,int_action_agent%self.n_cols)
#Schrittausführung
step_o, step_r, step_done = self.game.step(step_raw_col
Wenn die Aktion ausgeführt wird, werden der Brettstatus, Belohnungen und Flaggen, ob das Spiel abgeschlossen ist, übergeben. Zu diesem Zeitpunkt wurde das g_board von Game_Reversi mit den Agenten- und Umgebungselementen aktualisiert. Verwenden Sie in diesem Status die Funktion "build_map_from_game", um einen Board-Status zu erstellen.
Schließlich werden der Board-Status, die Belohnung und ob sie abgerechnet werden oder nicht, in der Instanz rot der Klasse Reward_observation_terminal von RL_Glue gespeichert und an RL_Glue zurückgegeben.
environment.py
# (Unterlassung)
rot = Reward_observation_terminal()
# build_map_from_game()Erstellen Sie eine Karte mit.
self.map=self.build_map_from_game()
observation = Observation()
observation.intArray = self.map
rot.o = observation
# step_r ist eine Belohnung, Schritt_Fertig ist, ob fortgefahren werden soll oder nicht
rot.r=step_r
rot.terminal = step_done
# (Unterlassung)
#Wenn es erledigt ist, der Agent des Agenten_end
#Wenn es keine Abrechnung gibt, der Agent des Agenten_Weiter zu Schritt
return rot
experiment.py Es ist ein "Experiment", das das Spiel verwaltet. Usage:
Description: Dieses Skript wurde gegenüber dem Inhalt des Buches "Implementation Deep Learning" nicht geändert, daher werde ich die Erklärung weglassen.
Game_Reversi.py Implementierung des Othello-Spiels. Usage:
Description: Implementiert die Regeln des Othello-Spiels. Gemäß den offiziellen Regeln von Othello ist der Platz zum Platzieren des Stücks auf den Ort beschränkt, an dem das Stück des Gegners umgedreht werden kann. Außerdem ist der Pass auf den Fall beschränkt, dass kein Platz für Ihr eigenes Stück vorhanden ist. Dieses Spiel folgt auch diesen Regeln.
Das Spielbrett wird durch ein Numpy Int-Array dargestellt. Aus diesem Grund wird die Hauptlogik mithilfe von Array-Operationen implementiert. [^ 2]
Der Betriebsablauf ist wie folgt:
Initialisierungscode.
Game_Reversi.py
def __init__(self,n_rows,n_cols):
#Board zurückgesetzt
self.n_rows = n_rows
self.n_cols = n_cols
self.g_board=np.zeros([self.n_rows,self.n_cols],dtype=np.int16)
#Othello stellt die ersten 4 Frames in die Mitte
self.g_board[self.n_rows//2-1,self.n_cols//2-1]=1
self.g_board[self.n_rows//2-1,self.n_cols//2]=-1
self.g_board[self.n_rows//2,self.n_cols//2-1]=-1
self.g_board[self.n_rows//2,self.n_cols//2]=1
Wie Sie sehen können, ist es möglich, nicht quadratische Bretter zu handhaben, aber derzeit versuchen wir, das Spiel nur mit Quadraten zu starten.
Um eine Figur zu platzieren und die gegnerische Figur zurückzugeben, müssen Sie zuerst den Ort angeben, an dem Sie die Figur platzieren können. Dies geschieht mit isValidMove (). isValidMove () verwendet eine Array-Operation, um zu bestimmen, wo ein Frame platziert werden soll. Beispielsweise versucht ● auf einer 8x8-Karte ●, einen Frame an der folgenden X-Position (2,1) zu platzieren.
Bestimmen Sie, ob Sie einen Rahmen an dieser Stelle gemäß dem folgenden Ablauf platzieren können. Phase1: Zunächst wird an der angegebenen Stelle in 8 Richtungen nach benachbarten Frames gesucht. Überprüfen Sie, ob sich an der grün hervorgehobenen Stelle ein Kreis befindet. Wenn zu diesem Zeitpunkt kein ○ vorhanden ist, wird beurteilt, dass ● nicht an dieser Stelle platziert werden kann, und der Prozess endet. In diesem Fall befindet sich an der rot dargestellten Stelle ein Kreis. Fahren Sie daher mit Phase 2 fort.
Phase2: Suchen Sie nach ● in Richtung ○ gefunden. Der Suchbereich wird gelb hervorgehoben. Die Suche wird in Richtung des Kreises fortgesetzt und fortgesetzt, bis Sie ein ●, ein Leerzeichen oder den Rand der Tafel finden. Zu Beginn wird für den Kreis in (3,2) unten ein Leerzeichen in (5,4) gefunden, sodass dieser Kreis nicht zurückgegeben werden kann. Als nächstes ungefähr ○ in (2,2). Wenn Sie nach rechts suchen, finden Sie ● bei (2,5). An dieser Stelle können Sie ein ● an der X-Position (2,1) platzieren.
Der folgende Code implementiert diese.
Game_Reversi.py
#Checken Sie vom angegebenen Ort aus in alle Richtungen ein.
#Endet, wenn mindestens eine gegnerische Figur zurückgegeben werden kann
for direction in ([-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]):
if not (0 <= (pos+direction)[0] < self.n_rows and 0 <= (pos+direction)[1] < self.n_cols ) :
#Überspringen Sie die Verarbeitung außerhalb des Bereichs
continue
#
# Phase 1:Ist die Farbe des angrenzenden Rahmens das Gegenteil der angegebenen Farbe?
#
cpos = pos + direction
if (self.g_board[tuple(cpos)] == -c):
#
# Phase 2:Gibt es da drüben deinen eigenen Rahmen?
#
while (0 <= cpos [0] < self.n_rows and 0 <= cpos [1] < self.n_cols):
if (self.g_board[tuple(cpos)] == 0):
#Es endet, weil es ein leerer Raum ist, bevor das Urteil gefällt wird
break
elif (self.g_board[tuple(cpos)] == -c):
#Wenn Sie in Zukunft einen eigenen Rahmen haben, können Sie ihn möglicherweise übernehmen.
#Suchen Sie weiter nach Ihrem eigenen Rahmen.
cpos = cpos+direction
continue
elif (self.g_board[tuple(cpos)] == c):
#Da mindestens ein Frame zurückgegeben werden kann, endet die Suche an diesem Punkt.
result=True
break
else:
print("catastorophic failure!!! @ isValidMove")
exit()
Wenn Sie isValidMove () für alle leeren Frames ausführen, erhalten Sie eine Liste, wo Sie die Frames platzieren können. Dies wird mit der Funktion getPositionAvail () implementiert.
def getPositionAvail(self,c):
temp=np.vstack(np.where(self.g_board==0))
nullTiles=np.hstack((temp[0].reshape(-1,1),temp[1].reshape(-1,1)))
#IsValidMove für Quadrate ohne Frames()
can_put=[]
for p_pos in nullTiles:
if self.isValidMove(p_pos[0],p_pos[1],c):
can_put.append(p_pos)
return can_put
Sowohl der Agent als auch die Umgebung wählen den Ort, an dem das Stück platziert werden soll, aus der Liste der Orte aus, an denen dieses Stück platziert werden kann. Zusätzlich kann diese Funktion auch eine Liste von Stellen erhalten, an denen der Gegner einen Frame platzieren kann, indem er einfach den Code des Frames umkehrt.
Drehen Sie den Rahmen mit putStone () um. Ähnlich wie bei isValidMove sucht diese Funktion nach Frames in 8 Richtungen und führt Phase 1 und Phase 2 aus. Wenn Sie ein Stück in der gleichen Farbe wie Ihr eigenes Stück finden, kehren Sie zu der Stelle zurück, an der Sie das Stück platziert haben, und drehen Sie es um. Im vorherigen Beispiel sind die Rahmen in (2,1) platziert und die gleichen Farbrahmen sind in (2,5) enthalten, sodass die Rahmen in der Reihenfolge (2,4), (2,3), (2,2) liegen. Umdrehen. Unten ist der Code.
Game_Reversi.py
for dir in ([-1,-1],[-1,0],[-1,1],[ 0,-1],[ 0,1],[ 1,-1],[ 1,0],[ 1,1]):
f_canFlip=False
cpos = pos + dir
#(Unterlassung)
#Gibt einen Frame von der aktuellen cpos-Position an die angegebene Position zurück.
if f_canFlip :
cpos=cpos-dir #Erster Frame, der zurückkehrt
while np.array_equal(cpos,pos) == False:
board[tuple(cpos)] = c
numFlip = numFlip + 1
cpos=cpos-dir
Diese Funktion gibt die Anzahl der gespiegelten Frames zurück. Wenn das 4. Argument True ist, wird der Modus simuliert und der gespiegelte Inhalt wird nicht im Spiel wiedergegeben. Dies wird verwendet, wenn Sie im Voraus prüfen möchten, wo die Frames platziert werden sollen und wie viele Frames umgedreht werden können. Diese Funktion kann auch die Farbe des gespiegelten Rahmens ändern, indem Sie einfach das Vorzeichen umkehren.
Der Agent verwendet DQN, um zu bestimmen, wo die Teile platziert werden sollen, während die Umgebung getPosition () verwendet, um zu bestimmen, wo die Teile platziert werden sollen. Die Logik von getPosition () bestimmt die Stärke von Othello. In diesem Code wird der Ort, an dem der Frame platziert werden soll, durch die folgende Logik festgelegt.
Wahrscheinlichkeit | Ort |
---|---|
90% | Jede der vier Ecken |
80% | Position, an der Sie die meisten Frames erhalten können |
10% or 20% | zufällig(Wenn ein Stück irgendwo in den vier Ecken platziert werden kann 10%Wenn Sie es nicht sagen können 20%) |
Unten ist der Code.
Game_Reversi.py
#Entscheide, ob du es zufällig machen willst
t_rnd=np.random.random()
# 1.90 wenn es Hörner gibt%Nehmen Sie dort mit einer Wahrscheinlichkeit von
if cornerPos != []:
if t_rnd < 0.9:
returnPos= cornerPos[np.random.randint(0,len(cornerPos))]
# 2.Dann 80%Holen Sie sich die mit der höchsten Nummer.
if returnPos==[]:
if maxPos != []:
if t_rnd < 0.8:
returnPos= maxPos
# 3.Zufällig, wenn an dieser Stelle nicht entschieden(Immerhin 1,Es besteht die Möglichkeit, dass es 2 sein wird)
if returnPos==[]:
returnPos= can_put[np.random.randint(0,len(can_put))]
return returnPos
Wenn die Aktionen sowohl des Agenten als auch der Umgebung abgeschlossen sind, werden das Endurteil und die Berechnung der Belohnung durchgeführt. Die endgültige Beurteilung erfolgt durch Finden eines Ortes, an dem beide Rahmen platziert werden können. Das Spiel endet, wenn es keinen Platz für beide gibt. Die Belohnung ist 0, während das Spiel läuft, wenn das Spiel vorbei ist, wird die Anzahl beider Teile gezählt, und wenn sie gleich sind, bedeutet dies "Unentschieden" -0,5, wenn es viele Agenten gibt, bedeutet dies "gewinnen" 1,0 und es gibt nur wenige Agenten. Zum Beispiel gibt "Verlieren" eine Belohnung von -1,0. Der Code lautet wie folgt.
Game_Reversi.py
stonePos_agent = self.getPosition(self.turn)
stonePos_environment = self.getPosition(-self.turn)
if stonePos_agent==[] and stonePos_environment==[]:
done=True
if self.render : print("****************Finish****************")
if done :
# 2.Ausgleichsberechnung nach Abschluss
num_agent=len(np.where(self.g_board==self.turn)[1])
num_envionment=len(np.where(self.g_board==-self.turn)[1])
if self.render : print("you:%i/environment:%i" % (num_agent,num_envionment))
#Beurteilung
if num_agent > num_envionment :
reward=1.0
if self.render : print("you win!")
elif num_agent < num_envionment :
reward=-1.0
if self.render : print("you lose!")
else :
reward=-0.5
if self.render : print("Draw!")
Game_Reversi_Test.py Es dient zum Testen der obigen "Game_Reversi.py". Ermöglicht es Ihnen, im Auftrag von Agenten gegen Menschen zu spielen. Usage:
python Game_Reversi_Test.py
Description: Die Platinengröße ist wie unten gezeigt bei 8x8 fest codiert. Ändern Sie sie daher entsprechend.
Game_Reversi_Test.py
g=game.Game_Reversi(8,8)
Beim Start wartet es auf Benutzereingaben. Geben Sie die Position an, an der der Frame mit einer durch Kommas getrennten Ganzzahl wie "2,4" platziert werden soll. Platzieren Sie das Stück wie bei der Reaktion auf den Agenten an der angegebenen Stelle und platzieren Sie Ihr eigenes Stück. Und wieder wird es auf Eingabe warten. Wenn beide Parteien am Ende kein Stück platzieren können, werden das Endurteil und die Punktzahl angezeigt und der Prozess endet.
Das ist alles für die Erklärung. Wir freuen uns darauf, Ihnen zu helfen. Wenn Sie Fehler, Kommentare, Fragen oder Fragen haben, würden wir uns freuen, wenn Sie dies kommentieren könnten.
(Buch) Implementierung Deep Learning
http://shop.ohmsha.co.jp/shopdetail/000000004775/
Othello regiert
http://www.othello.org/lesson/lesson/rule.html
(Andere Referenzen werden zu einem späteren Zeitpunkt veröffentlicht)
[^ 1]: Da agent_init jedoch Informationen von RL_Glue empfängt, benötigen Sie dieses Argument möglicherweise nicht, wenn Sie es verwenden? [^ 2]: Aus diesem Grund denke ich, dass es auch möglich ist, Cuda's Numpy (cupy) zu verwenden. [^ 3]: Wenn übrigens die Farbe des First Movers, dh der Agent (= 1), gemäß der offiziellen Regel gemäß der von der "Japan Othello Federation" festgelegten Regel schwarz ist, ist die Anordnung der Anfangsrahmen in dieser Klasse in Schwarzweiß umgekehrt. Korrekt. Es gibt jedoch keine theoretische Änderung, wenn Sie den Aspekt nur um 90 Grad drehen. Lassen Sie ihn also unverändert.