===Protokoll=== Heute haben wir versucht den Code, den wir letzte Woche gefunden haben, auf unser selbst gebautes Spiel zu übertragen. Hierbei lernt die Ai, indem alle möglichen Zustände und die Werte, also ob der Zustand gut oder schlecht ist, in einer Tabelle stehen und die Ai so einfach bestimmen kann, was der beste Zug ist, indem sie einfach den zustand mit dem höchsten Wert und somit der höchsten Gewinnwahrscheinlichkeit auswählt. Da unser Spiel ganz andere Methoden verwendet, als das, was wir gefunden haben, ist uns die Umstellung nicht so leicht gefallen. So ist unter anderem folgender unvollständiger Code entstanden. import turtle as t import numpy as np from math import inf as infinity import itertools import random def play_move(state, player, block_num):#Hier wird der ausgewählte Zug in die Matrix übertragen if state[int((block_num-1)/3)][(block_num-1)%3] is ' ': state[int((block_num-1)/3)][(block_num-1)%3] = player if(player == 'X'): printX(int((block_num-1)/3), (block_num-1)%3) else: print0(int((block_num-1)/3), (block_num-1)%3) def copy_game_state(state):#Der Spielzustand wird kopiert new_state = [[' ',' ',' '],[' ',' ',' '],[' ',' ',' ']] for i in range(3): for j in range(3): new_state[i][j] = state[i][j] return new_state def check_current_state(game_state):#Überprüfung ob wer gewonnen hat oder das Spiel unentschieden ausgegangen ist # Check horizontals if (game_state[0][0] == game_state[0][1] and game_state[0][1] == game_state[0][2] and game_state[0][0] is not ' '): return game_state[0][0], "Done" if (game_state[1][0] == game_state[1][1] and game_state[1][1] == game_state[1][2] and game_state[1][0] is not ' '): return game_state[1][0], "Done" if (game_state[2][0] == game_state[2][1] and game_state[2][1] == game_state[2][2] and game_state[2][0] is not ' '): return game_state[2][0], "Done" # Check verticals if (game_state[0][0] == game_state[1][0] and game_state[1][0] == game_state[2][0] and game_state[0][0] is not ' '): return game_state[0][0], "Done" if (game_state[0][1] == game_state[1][1] and game_state[1][1] == game_state[2][1] and game_state[0][1] is not ' '): return game_state[0][1], "Done" if (game_state[0][2] == game_state[1][2] and game_state[1][2] == game_state[2][2] and game_state[0][2] is not ' '): return game_state[0][2], "Done" # Check diagonals if (game_state[0][0] == game_state[1][1] and game_state[1][1] == game_state[2][2] and game_state[0][0] is not ' '): return game_state[1][1], "Done" if (game_state[2][0] == game_state[1][1] and game_state[1][1] == game_state[0][2] and game_state[2][0] is not ' '): return game_state[1][1], "Done" # Check if draw draw_flag = 0 for i in range(3): for j in range(3): if game_state[i][j] is ' ': draw_flag = 1 if draw_flag is 0: return None, "Draw" return None, "Not Done" def comstarts():#Aufruf damit der Computer anfängt zu spielen global playerChoice newGame('O') playerChoice = 0 def playerstarts():#Aufruf, damit man selber anfangen kann global playerChoice newGame('X') playerChoice = 1 def newGame(player_choice):#Es wird "aufgeräumt" und Platz für ein neues Spiel gemacht global NoWin global WinsPlayer global WinsCom if(check_current_state(game_state)== "Done" or NoWin == 0 and WinsPlayer == 0 and WinsCom == 0): start.up() start.clear() start.setx(-2) start.hideturtle() start.write(WinsPlayer) start.sety(start.ycor()+2) start.write(WinsCom) start.sety(start.ycor()+2) start.write(NoWin) t.clear() if(player_choice == 'O'): block_choice = getBestMove(game_state, players[0]) play_move(game_state ,players[0], block_choice) def wheretogo(x, y):#Prüft wo man hingesetzt hat ''' prüft, wo der Spieler hinklickt und gibt je nachdem eine Zahl zwischen 1 und 9 zurück. ''' global game_state if(x > 0 and x <=10): spalte = 1 if(x >10 and x <=20): spalte = 2 if(x >20 and x <=30): spalte = 3 if(y > 0 and y <=10): zeile = 0 if(y >10 and y <=20): zeile = 1 if(y >20 and y <=30): zeile = 2 block_choice = 3*zeile +spalte if(game_state[int((block_choice-1)/3)][(block_choice-1)%3] == ' '): t.up() t.setx((spalte-1)*10+5) t.sety(zeile*10+5) game(block_choice) def printX(zeile, spalte):#Zeigt ein X t.up() t.setx(spalte*10+5) t.sety(zeile*10+5) t.down() t.left(45) t.forward(5) t.backward(10) t.forward(5) t.left(90) t.forward(5) t.backward(10) t.forward(5) t.right(135) t.up() def print0(zeile, spalte):#Zeigt einen Kreis t.up() t.setx(spalte*10+5) t.sety(zeile*10+5) t.down() t.up() t.right(90) t.forward(4) t.left(90) t.down() t.circle(4) t.up() def tttfeld():#Grundlage für das ganze Tic Tac Toe Feld """ Malt ein 3x3 Feld. """ t.setworldcoordinates(0,30,30,0) spalten = t.Turtle() spalten.left(90) zeilen = t.Turtle() spalten.speed(0) zeilen.speed(0) spalten.hideturtle() zeilen.hideturtle() for i in range(4): spalten.setx(i*10) zeilen.sety(i*10) spalten.forward(30) zeilen.forward(30) spalten.sety(0) zeilen.setx(0) zeilen.goto(0,0) spalten.goto(0,0) zeilen.up() zeilen.setx(-3) zeilen.write("Siege vom Spieler:", align="right") zeilen.sety(zeilen.ycor()+2) zeilen.write("Siege vom Computer:", align="right") zeilen.sety(zeilen.ycor()+2) zeilen.write("Unentschieden:", align="right") zeilen.goto(0,0) zeilen.goto(31,15) zeilen.write("Wer soll anfangen? Du(X) oder der Computer(O)?", align="left", font=("Arial", 8, "normal")) zeilen.down() def update_state_value(curr_state_idx, next_state_idx, learning_rate):#Die Ai passt den Wert für einen Zustand an new_value = state_values_for_AI[curr_state_idx] + learning_rate*(state_values_for_AI[next_state_idx] - state_values_for_AI[curr_state_idx]) state_values_for_AI[curr_state_idx] = new_value def getBestMove(state, player):#Hier wird der beste Zug für die Ai ermittelt ''' Reinforcement Learning Algorithm ''' moves = [] curr_state_values = [] empty_cells = [] for i in range(3):#Hier werden die leeren Felder bestimmt for j in range(3): if state[i][j] is ' ': empty_cells.append(i*3 + (j+1)) for empty_cell in empty_cells:#Hier wird jeder mögliche Zug überprüft moves.append(empty_cell) new_state = copy_game_state(state) #play_move(new_state, player, empty_cell) next_state_idx = list(states_dict.keys())[list(states_dict.values()).index(new_state)] curr_state_values.append(state_values_for_AI[next_state_idx]) best_move_idx = np.argmax(curr_state_values)#Und hier der Zug mit dem höchsten Wert, also der beste Zug ermittelt best_move = moves[best_move_idx] return best_move def game(block_choice):#Das eigentliche Spiel global NoWin global WinsPlayer global WinsCom global game_state global playerChoice current_player_idx=playerChoice current_state = "Not Done" winner = None if current_player_idx == 0: player_choice = 'X' else: player_choice = 'O' curr_state_idx = list(states_dict.keys())[list(states_dict.values()).index(game_state)] if current_player_idx == 0: #Mensch macht ersten Zug play_move(game_state ,players[current_player_idx], block_choice) current_player_idx = 1 block_choice = getBestMove(game_state, players[current_player_idx]) play_move(game_state ,players[current_player_idx], block_choice) else: #Computer macht ersten Zug play_move(game_state ,players[current_player_idx], block_choice) current_player_idx = 0 block_choice = getBestMove(game_state, players[current_player_idx]) play_move(game_state ,players[current_player_idx], block_choice) print(game_state) winner, current_state = check_current_state(game_state) if winner is not None: t.up() t.goto(15,15) if(str(winner) == player_choice): WinsPlayer = WinsPlayer +1 t.write("Du hast gewonnen", align="center", font=("Arial", 32, "normal")) else: WinsCom = WinsCom +1 t.write("Du hast verloren", align="center", font=("Arial", 32, "normal")) t.goto(40,15) t.write("Wer soll anfangen? Du(X) oder der Computer(O)?", align="left", font=("Arial", 8, "normal")) if current_state is "Draw": NoWin = NoWin +1 t.up() t.goto(15,15) t.write("Unentschieden", align="center", font=("Arial", 30, "normal")) t.goto(40,15) t.write("Wer soll anfangen? Du(X) oder der Computer(O)?", align="left", font=("Arial", 8, "normal")) # PLaying # Initialize state values player = ['X','O',' '] states_dict = {} all_possible_states = [[list(i[0:3]),list(i[3:6]),list(i[6:10])] for i in itertools.product(player, repeat = 9)] n_states = len(all_possible_states) # 2 players, 9 spaces n_actions = 9 # 9 spaces state_values_for_AI = np.full((n_states),0.0) print("n_states = %i \nn_actions = %i"%(n_states, n_actions)) for i in range(n_states): states_dict[i] = all_possible_states[i] winner, _ = check_current_state(states_dict[i]) if winner == 'O': # AI won state_values_for_AI[i] = 1 elif winner == 'X': # AI lost state_values_for_AI[i] = -1 start = t.Turtle() start.speed(0) t.speed(0) t.hideturtle() screen=t.Screen() WinsPlayer = 0 WinsCom = 0 NoWin = 0 playerChoice = 0 game_state = [[' ',' ',' '], [' ',' ',' '], [' ',' ',' ']] players = ['X','O'] current_state = "Not Done" tttfeld() #LOAD TRAINED STATE VALUES state_values_for_AI = np.loadtxt('trained_state_values_O.txt', dtype=np.float64) screen.onclick(wheretogo) screen.onkeypress(comstarts, "o") screen.onkeypress(playerstarts, "x") screen.listen() t.mainloop() t.exitonclick() **Protokoll für den 23.1.2020** Wir haben angefangen eine Funkiton zu schreiben um die umgebung eines neu gesetzten Stein zu prüfen und festzustellen ob dadurch eine Prtei gewinnt. Funktion zum checken von jedem Array: def Summe(self, zeile, spalte): #muss noch mit der umgebung verknüpft werden for i in range(len(a)-3): b=a[i:i+4] if (sum(b)== 4 or sum(b)==-4): return sum(b) Spalte: a=board[:6, s-1:s] Diagonale: a=np.diagonal(board, s-z) Zeile: a=board[:1] #für die Zeile **Protokoll für den 06.02.2020** Training Algorithmus fertig Funktion fertig - Wissen ob 4 in einer Reihe sind Problem: Anzahl möglicher Zustände über 10 hoch 20 D.h. das Programm läuft so noch nicht Kommentierte Version des Codes mit dem NN: import random import gym import numpy as np from collections import deque from keras.models import Sequential from keras.layers import Dense, Dropout from keras.optimizers import Adam import keras def ViergewinntAnfangsmatrix(): #game_state[x,0,0] greift auf das zu benutzende Feld zu (x=0/1 sind Spiele; x=2 zeigt leere Felder) #game_state[0,x,0] greift auf die Zeile zu #game_state[0,0,x] greift auf die Spalte zu game_state = np.zeros_like(np.arange(42*3).reshape(3,6,7)) game_state[2]=1 return game_state def check_array(a): #Prüft ob in einem Array 4 in einer Reihe sind for i in range(len(a)-3): b=sum(a[i:i+4]) if (b== 4): return 4 def checkgewonnen(game_state): #Prüft ob jemand gewonnen hat Winstate = game_state[0] Losestate = game_state[1] Drawstate = game_state[2] #Spalte for i in range(7): a=check_array(Winstate[:6, i:i+1].reshape(6,)) b=check_array(Losestate[:6, i:i+1].reshape(6,)) if(a==4):return "Win" if(b==4):return "Lose" #Diagonalen: for i in range(3): a=check_array(np.diagonal(Winstate, i+1)) b=check_array(np.diagonal(Losestate, i+1)) if(a==4):return "Win" if(b==4):return "Lose" a=check_array(np.diagonal(Winstate, -i)) b=check_array(np.diagonal(Losestate, -i)) if(a==4):return "Win" if(b==4):return "Lose" a=check_array(np.fliplr(Winstate).diagonal(i+1)) b=check_array(np.fliplr(Losestate).diagonal(i+1)) if(a==4):return "Win" if(b==4):return "Lose" a=check_array(np.fliplr(Winstate).diagonal(-i)) b=check_array(np.fliplr(Losestate).diagonal(-i)) if(a==4):return "Win" if(b==4):return "Lose" #Zeile: for i in range(6): a=check_array(Winstate[i:i+1].reshape(7,)) b=check_array(Losestate[i:i+1].reshape(7,)) if(a==4):return "Win" if(b==4):return "Lose" # Check if draw for i in range(7): if(Drawstate[0,i] == 1): return None return "Draw" class Agent: def __init__(self, state_size, action_size): self.state_size = state_size self.action_size = action_size self.memory = deque(maxlen=2000) #Erinnerungen self.gamma = 1.0 # discount rate self.epsilon = 1.0 # exploration rate self.epsilon_min = 0.01 self.epsilon_decay = 0.999 self.learning_rate = 0.001 self.model = self._build_model() #NN wird gebaut def _build_model(self): # Einfaches NN #Müssen weitere Layer hinzugefügt werden, da 4gewinnt komplexer ist? model = Sequential() #Standard komplett vernetztes NN model.add(Dense(48, input_dim=self.state_size, activation='relu', kernel_regularizer=keras.regularizers.l2(0.00001)))#Input muss angepasst werden model.add(Dropout(0.3)) # Soll "Overfitting" vermeiden model.add(Dense(24, activation='relu', kernel_regularizer=keras.regularizers.l2(0.00001)))#Hidden-Layer model.add(Dense(self.action_size, activation='linear'))#Output im Bezug auf mögliche Aktionen (hier 7) model.compile(loss='mse', optimizer=Adam(lr=self.learning_rate)) #Erstellung return model def remember(self, state, action, reward, next_state, done):#Erinnerungsmöglichkeit # merkt sich alle bisher durchlaufenen Zustände self.memory.append((state, action, reward, next_state, done)) def act(self, state): # epsilon-greedy: off-policy oder policy if np.random.rand() <= self.epsilon:#Zufällige Aktion return random.randrange(self.action_size) act_values = self.model.predict(state)#Vorhersage des NN return np.argmax(act_values[0]) # returns action def replay(self, batch_size): # baut den Vektor der Q-Werte aus # als reward zum Zeitpunkt t + gamma*max(moegliche rewards zum Zeitpunkt t+1) #prüft ob ein Zug gut war? minibatch = random.sample(self.memory, batch_size) states, targets_f = [], [] for state, action, reward, next_state, done in minibatch: target = reward #Reward-Anpassung if not done: target = (reward + self.gamma * np.amax(self.model.predict(next_state)[0])) target_f = self.model.predict(state) target_f[0][action] = target # Filtering out states and targets for training states.append(state[0]) targets_f.append(target_f[0]) history = self.model.fit(np.array(states), np.array(targets_f), epochs=1, verbose=0) # Keeping track of loss loss = history.history['loss'][0] if self.epsilon > self.epsilon_min: self.epsilon *= self.epsilon_decay return loss #laden und speichern des NN def load(self, name): self.model.load_weights(name) def save(self, name): self.model.save_weights(name) # Lernen: # EPISODES mal durchspielen, bis der Mast umfaellt EPISODES = 10 env = gym.make('CartPole-v1') #4gewinnt einfuegen #state_size = env.observation_space.shape[0] state_size = 7 #Für die 7 Möglichkeiten die es gibt einen Stein zu platzieren #Spielfeld? action_size = env.action_space.n #Wird 7 sein, solange keine Spalte voll ist agent = Agent(state_size, action_size) done = False batch_size = 32#Muss eventuell auch angepasst werden for e in range(EPISODES): state = env.reset() #eigene Reset Funktion state = np.reshape(state, [1, state_size])#Erzeugt einen Array, der den Zustand repräsentiert cum_reward = 0 for time in range(500):#Zeit braucht man nicht bei 4 gewinnt, ersetzen mit max Spiellänge #env.render() action = agent.act(state)#Auswahl der möglichen Aktionen next_state, reward, done, _ = env.step(action)#Ausführung dieser Aktion reward = reward/(abs(next_state[0])+1.)**2 if not done else -10 #Anpssung des rewards-System cum_reward += reward next_state = np.reshape(next_state, [1, state_size])#nächsten Zustand herbei führen agent.remember(state, action, reward, next_state, done)#merken state = next_state if done: print(("episode: {}/{}, score: {}, e: {:.2}"#Score sollte nicht abhängig von Anzahl Züge, sondern von gewonenen/verloren sein .format(e, EPISODES, time, agent.epsilon))) break if len(agent.memory) > batch_size: loss = agent.replay(batch_size) # Logging training loss and actual reward every 10 timesteps if time % 10 == 0: print(("episode: {}/{}, time: {}, cumulative reward: {:.4f}, loss: {:.4f}".format(e, EPISODES, time, cum_reward, loss))) agent.save("qlearning_cartpole.weights") # Testen des gelernten Modells mit leichten Störungen, um die Stabilität zu sehen. Mit Visualisierung #Funktioniert hier nicht so richtig #2 Ai's gegeneinander spielen lassen? agent.load("qlearning_cartpole.weights")#gemerkte Entscheidungen laden, um damit gute Züge machen zu können import time as ti #Zeit braucht man nicht for e in range(1000):#Von hier state = env.reset() state[0] = state[0] + np.random.randn()*0.1 state[1] = state[1] + np.random.randn()*0.1 state[2] = state[2] + np.random.randn()*0.1 state[3] = state[3] + np.random.randn()*0.1 env.env.state = state state = np.reshape(state, [1, state_size]) for time in range(2000):#bis hier muss komplett ersetzt werden env.render()#Nicht Zeit abhängig, braucht man also nicht agent.epsilon = 0# Setzt Zufallshandlung auf 0 action = agent.act(state) next_state, reward, done, _ = env.step(action) next_state = np.reshape(next_state, [1, state_size]) state = next_state#Spielt einen Zug durch if done: print("Duration: ", time) break else: print("Volle Zeit")#Hier wichtig ob Gewonnen/Verloren/Unentschieden? Vorheriger Termin: [[some:19.12.2019]]\\ Nächster Termin: [[some:23.01.2020]]