From 9825def2536f50708ca3e21d126a832dbef72095 Mon Sep 17 00:00:00 2001 From: Simeon Keske Date: Wed, 7 Aug 2019 21:49:40 +0200 Subject: [PATCH] Initial commit --- .gitignore | 2 + PvN.py | 43 +++++++++++++++++ PvP.py | 51 ++++++++++++++++++++ fiar.py | 121 +++++++++++++++++++++++++++++++++++++++++++++++ lib.py | 58 +++++++++++++++++++++++ nn.py | 40 ++++++++++++++++ requirements.txt | 2 + training.py | 63 ++++++++++++++++++++++++ 8 files changed, 380 insertions(+) create mode 100644 .gitignore create mode 100644 PvN.py create mode 100644 PvP.py create mode 100644 fiar.py create mode 100644 lib.py create mode 100644 nn.py create mode 100644 requirements.txt create mode 100644 training.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee1675f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.fiar +__pycache__ diff --git a/PvN.py b/PvN.py new file mode 100644 index 0000000..3e82e33 --- /dev/null +++ b/PvN.py @@ -0,0 +1,43 @@ +import fiar +import nn +import lib + +game = fiar.FIAR(); +nn = lib.loadSingle(0) + +def declareWinner(): + game.printField(); + print("And the winner is P", game.proveWinner(), "!") + +def isnumber(val): + try: + val = int(val) + return True + except ValueError: + return False + + +while True: + game.printField(); + + player_input = input("P1, where do you put your stone? ") + + while not (isnumber(player_input) and 7 >= int(player_input) >= 1): + player_input = input("P1, your input has to be a number between 1 and 7! New input: ") + + try: + game.setStone(int(player_input)-1, 1) + except OverflowError: + print("This Column is already full, maybe try again next time...") + + if(game.proveWinner()): + declareWinner() + break + + # --------------- # + + game = lib.nextNNmove(nn, game, 2); + + if(game.proveWinner()): + declareWinner() + break diff --git a/PvP.py b/PvP.py new file mode 100644 index 0000000..4420bc4 --- /dev/null +++ b/PvP.py @@ -0,0 +1,51 @@ +import fiar + +game = fiar.FIAR(); + +def declareWinner(): + game.printField(); + print("And the winner is P", game.proveWinner(), "!") + + +def isnumber(val): + try: + val = int(val) + return True + except ValueError: + return False + + +while True: + game.printField(); + + player_input = input("P1, where do you put your stone? ") + + while not (isnumber(player_input) and 7 >= int(player_input) >= 1): + player_input = input("P1, your input has to be a number between 1 and 7! New input: ") + + try: + game.setStone(int(player_input)-1, 1); + except OverflowError: + print("This Column is already full, maybe try again next time...") + + if(game.proveWinner()): + declareWinner() + break + + # --------------- # + + game.printField(); + + player_input = input("P2, where do you put your stone? ") + + while not (isnumber(player_input) and 7 >= int(player_input) >= 1): + player_input = input("P2, your input has to be a number between 1 and 7! New input: ") + + try: + game.setStone(int(player_input)-1, 2); + except OverflowError: + print("This Column is already full, maybe try again next time...") + + if(game.proveWinner()): + declareWinner() + break diff --git a/fiar.py b/fiar.py new file mode 100644 index 0000000..2d72d8c --- /dev/null +++ b/fiar.py @@ -0,0 +1,121 @@ +class FIAR(): + def __init__(self, height=6, width=7): + self.height = height # y + self.width = width # x + self.field = [] + # field[x][y] + for x in range(width): + col = [] + for y in range(height): + col.append(None) + self.field.append(col) + + def getTransformedField(self): + output = [] + for col in self.field: + output += col + return output + + def printField(self): + redBG = "\33[41m" + greenBG = "\33[42m" + clearBG = "\33[0m" + dashes = (self.width * 2)+1 + print("-" * dashes) + for y in range(self.height, -1, -1): + for x in range(self.width): + if y == self.height: + print("|" + str(x+1), end='') + elif self.field[x][y] == 1: + print("|" + redBG + "x" + clearBG, end='') + elif self.field[x][y] == 2: + print("|" + greenBG+"o" + clearBG, end='') + else: + print("|" + " ", end='') + print("|") + print("-" * dashes) + + def setStone(self, col, player): + if(col in list(range(self.width))): + has_set = False + for i in range(self.height): + if self.field[col][i] == None: + self.field[col][i] = player + has_set = True + break + if not has_set: + raise OverflowError + else: + raise IndexError + + def proveWinner(self): + # Tie: + tie = True + for x in range(self.width): + if(self.field[x][self.height-1] == None): + tie = False + break + if tie: + return 3 + + # Horizontal: + for y in range(self.height): + for x in range(self.width-3): + if self.field[x][y] != None: + first = self.field[x][y] + isWinner = True + + for i in range(1, 4): + if self.field[x+i][y] != first: + isWinner = False + break + + if isWinner: + return first + + # Vertical: + for x in range(self.width): + for y in range(self.height-3): + if self.field[x][y] != None: + first = self.field[x][y] + isWinner = True + + for i in range(1, 4): + if self.field[x][y+i] != first: + isWinner = False + break + + if isWinner: + return first + + # Diagonal Up: + for x in range(self.width-3): + for y in range(self.height-3): + if self.field[x][y] != None: + first = self.field[x][y] + isWinner = True + + for i in range(1, 4): + if self.field[x+i][y+i] != first: + isWinner = False + break + + if isWinner: + return first + + # Diagonal Down: + for x in range(3, self.width): + for y in range(self.height-3): + if self.field[x][y] != None: + first = self.field[x][y] + isWinner = True + + for i in range(1, 4): + if self.field[x-i][y+i] != first: + isWinner = False + break + + if isWinner: + return first + + return False diff --git a/lib.py b/lib.py new file mode 100644 index 0000000..411c976 --- /dev/null +++ b/lib.py @@ -0,0 +1,58 @@ +import nn +import pickle + +networks_filename = "networks.fiar" + +def nextNNmove(network, game, player): + flatfield = game.getTransformedField() + player1 = [] + player2 = [] + for spot in flatfield: + if spot == 1: + player1.append(1) + player2.append(0) + elif spot == 2: + player1.append(0) + player2.append(1) + else: + player1.append(0) + player2.append(0) + + out = network.calculateOutput(player1+player2) + preferences = [] + for i in range(len(out)): + maxI = 0 + maxVal = -20 + for j in range(len(out)): + if out[j] > maxVal: + maxVal = out[j] + maxI = j + out[maxI] = -20 + preferences.append(maxI) + + for val in preferences: + try: + game.setStone(val, player) + except OverflowError: + pass + else: + break + return game + +def createAndSave(num): + nets = [] + for i in range(num): + nets.append(nn.NN(84, 168, 168, 84, 84, 42, 7)) + saveAll(nets) + +def saveAll(nets): + with open(networks_filename, 'wb') as networks_file: + pickle.dump(nets, networks_file) + +def loadAll(): + with open(networks_filename, 'rb') as networks_file: + nets = pickle.load(networks_file) + return nets + +def loadSingle(num): + return loadAll()[num] diff --git a/nn.py b/nn.py new file mode 100644 index 0000000..91e5705 --- /dev/null +++ b/nn.py @@ -0,0 +1,40 @@ +import numpy as np + +class NN(): + score = 0 + + def __init__(self, *args): + self.layers = args + self.synapses = [] + for i in range(len(self.layers)-1): + self.synapses.append(np.random.normal(scale=1,size=(self.layers[i], self.layers[i+1]))) + + def calculateOutput(self, input): + layerInput = input + + for synapseLayer in self.synapses: + layerInput = np.dot(layerInput, synapseLayer) + layerInput = self.sigmoid(layerInput) + + return layerInput + + def sigmoid(self, s): + #print("Arr:", arr) + #s = np.array(arr) + # activation function + res = 1 / (1 + np.exp(-s)) + #print("Res:", res) + return res + + def getSynapses(self): + return self.synapses + + def setSynapses(self, synapses): + self.synapses = synapses + + def mutate(self, val): + mut_arr = [] + for i in range(len(self.layers)-1): + mut_arr.append(np.random.normal(scale=val,size=(self.layers[i], self.layers[i+1]))) + self.synapses = np.add(self.synapses, mut_arr) + return mut_arr diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..44f27a2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy +pickle diff --git a/training.py b/training.py new file mode 100644 index 0000000..c91107c --- /dev/null +++ b/training.py @@ -0,0 +1,63 @@ +import lib, fiar + +nets = lib.loadAll() +gen = 0 + +def NvN(net1, net2, printField=False): + game = fiar.FIAR(); + + while True: + game = lib.nextNNmove(net1, game, 1); + if(game.proveWinner()): + break + game = lib.nextNNmove(net2, game, 2); + if(game.proveWinner()): + break + + if printField: + game.printField() + print(net1.synapses[0][0][0]) + print() + + return game.proveWinner() + +try: + while True: + gen += 1 + print("Gen:", gen) + + # Reset the nets' score: + for net in nets: + net.score = 0 + + # Let the nets play against each other: + for i in range(len(nets)): + for j in range(i+1, len(nets)): + + + winner = NvN(nets[i], nets[j], i==0 and j==1) + #print("Net1:",i,"Net2:",j) + #print("Winner:",winner) + if(winner == 1): + nets[i].score += 1 + if(winner == 2): + nets[j].score += 1 + + winner = NvN(nets[j], nets[i]) + #print("Net1:",i,"Net2:",j) + #print("Winner:",winner) + if(winner == 1): + nets[i].score += 1 + if(winner == 2): + nets[j].score += 1 + + # Sort the nets by their score: + nets.sort(key=lambda x: x.score, reverse=True) + for i in range(int(len(nets)/3)): + nets[i+int(len(nets)/3)] = nets[i]; + nets[i+int(len(nets)/3)].mutate(0.001); + nets[i+int(len(nets)/3*2)] = nets[i]; + nets[i+int(len(nets)/3*2)].mutate(0.1); +except KeyboardInterrupt: + print("Saving all Data...") + lib.saveAll(nets)