import networkx as nx
import matplotlib.pyplot as plt
from random import randint
from math import isclose
import numpy as np
import matplotlib
import matplotlib.gridspec as gridspec
from matplotlib.ticker import NullFormatter

nullfmt = NullFormatter()  # Keine Labels im Netzwerkplot

#plot settings
params = {
    'figure.figsize'    : [10, 7.5],
    'text.usetex'       : True,
    'legend.fontsize'   : 12
}
matplotlib.rcParams.update(params) 

######################################################################
#Beginn des Python Programms "Spatial Games"
######################################################################

#Festlegung der Auszahlungsmatrix des symmetrischen (2x2)-Spiels
# Dominant Game
a=1
b=0
c=1.64
d=0.01

# Definition der symmetrischen Spielmatrix (in reinen (sA,sB=0,1) und gemischten Strategien)
def Dollar(sA,sB,a,b,c,d):
    Dollar = a*sA*sB + b*sA*(1-sB) + c*(1-sA)*sB + d*(1-sA)*(1-sB)
    return Dollar

av_deg = []#Erzeugung einer Liste zum Speichern der gemittelten Verteilungsfunktion

#Erzeugung des regulaeren Netzwerkes zum Anfangszeitpunkt t=0 
#Jeder Knoten ist mit seinem naechsten Nachbarn auf einem 2-D Gitter verbunden
#Festlegung der Anfangsstrategien (Walker Anfangsbedingung)
G=nx.Graph()
Nx = 30
Ny = 30
NKn = Nx*Ny#Anzahl der Knoten
Nit = 32#Anzahl der Zeit/Iterationsschritte
PropPlayers = np.zeros([NKn,10])#Erzeugung einer Liste zum Speichern der Knoten/Spieler Eigenschaften
k = 0
for i in range(0,Nx):
    for j in range(0,Ny):
        G.add_node(k)
        PropPlayers[k,0] = k #Knotennummer
        PropPlayers[k,1] = i #x-Koordinate auf dem 2-D Gitter
        PropPlayers[k,2] = j #y-Koordinate auf dem 2-D Gitter
        if k == int(NKn/2 + Ny/2):
            PropPlayers[k,3] = 0 #Aktuelle Strategienwahl
            PropPlayers[k,6] = 0 #Zukuenftige Strategienwahl
        else:
            PropPlayers[k,3] = 1 #Aktuelle Strategienwahl
            PropPlayers[k,6] = 1 #Zukuenftige Strategienwahl
        k = k + 1

ColorPlayer=[]
k = 0
for i in range(0,Nx):
    for j in range(0,Ny):
        for l in range(0,NKn):
            if k != l and [PropPlayers[l,1],PropPlayers[l,2]] in [[i-1,j-1],[i-1,j],[i-1,j+1],[i,j-1],[i,j+1],[i+1,j-1],[i+1,j],[i+1,j+1]] and list(G.edges()).count((k,l))==0 and list(G.edges()).count((l,k))==0:
                G.add_edge(k,l)
            if k != l and j==0 and [PropPlayers[l,1],PropPlayers[l,2]] in [[i-1,Ny-1],[i,Ny-1],[i+1,Ny-1]]and list(G.edges()).count((k,l))==0 and list(G.edges()).count((l,k))==0:
                G.add_edge(k,l)
            if k != l and i==0 and [PropPlayers[l,1],PropPlayers[l,2]] in [[Nx-1,j-1],[Nx-1,j],[Nx-1,j+1]] and list(G.edges()).count((k,l))==0 and list(G.edges()).count((l,k))==0:
                G.add_edge(k,l)
            if k != l and i==0 and j==0 and [PropPlayers[l,1],PropPlayers[l,2]] in [[Nx-1,Ny-1]] and list(G.edges()).count((k,l))==0 and list(G.edges()).count((l,k))==0:
                G.add_edge(k,l)
            if k != l and i==0 and j==Ny-1 and [PropPlayers[l,1],PropPlayers[l,2]] in [[Nx-1,0]] and list(G.edges()).count((k,l))==0 and list(G.edges()).count((l,k))==0:
                G.add_edge(k,l)	
        k = k + 1

#Berechnung der Liste der Knotengrade
degree_sequence = []
for i in G.nodes():
    degree_sequence.append(G.degree(i))
av_deg.extend(degree_sequence) 
maxk = np.max(degree_sequence)
maxkav = np.max(av_deg)

#Erzeugung einer Liste zum Speichern der mittleren Knoten/Spieler Eigenschaften
AveragePropPlayers = np.zeros([Nit,2])
AveragePropPlayers[0,0] = np.sum(PropPlayers[:,3])/NKn
  
#Anfang der Schleife der zeitlichen Erzeugung des Netzwerks ( Nit Iterations-Zeit-Endpunkt )
for it in range(1,Nit):
    
    plt.figure(0)
    gs = gridspec.GridSpec(2, 2, width_ratios=[1,1], height_ratios=[1.2,2.0], hspace=0.1)
    ax1 = plt.subplot(gs[0])
    ax2 = plt.subplot(gs[1])
    ax3 = plt.subplot(gs[2])
    ax4 = plt.subplot(gs[3])
    props = dict(boxstyle='round', facecolor='white', alpha=0.92)

    #Achsenbeschriftung
    ax1.set_xlim(0,Nit-1)
    ax1.set_ylim(0,1)
    ax1.set_ylabel(r'$\rm x(t)$')
    ax2.set_ylabel(r'$\rm P(k)$')
    ax4.yaxis.set_major_formatter(nullfmt)
    ax4.xaxis.set_major_formatter(nullfmt)
    ax2.set_xlim(0,maxkav+2)
    ax2.set_ylim(0,1.05)
    ax2.set_ylim(0,1)
    ax3.set_xlim(-0.5,Nx-0.5)
    ax3.set_ylim(-0.5,Ny-0.5)

    #Farbenfestlegung der aktuellen Strategienwahl  
    ColorPlayer = []
    for kk in range(0,NKn):
        if PropPlayers[kk,3] == 0:
            ColorPlayer.extend("r")
        if PropPlayers[kk,3] == 1:
            ColorPlayer.extend("b")  

    #Plotten der einzelnen Teildiagramme
    sgross = 55
    sklein = 5
    ax1.plot(range(0,it,1),AveragePropPlayers[0:it,0], c="black")
    ax2.hist(degree_sequence,bins=range(0,int(maxk+2),1), align="left", histtype='bar', color="blue", alpha=0.4, edgecolor="black")
    ax3.scatter(PropPlayers[:,1],PropPlayers[:,2],s=sgross,c=ColorPlayer,marker="s",alpha=1,edgecolor='none') 

    #Erzeugung des Netzwerk-Bildes  
    node_size=250
    node_alpha=0.5
    edge_tickness=0.4
    edge_alpha=0.7
    edge_color="black"
    node_text_size=9
    text_font="sans-serif"
    graph_pos=nx.shell_layout(G)
    nx.draw_networkx_nodes(G,graph_pos,node_size=node_size,alpha=node_alpha, node_color=ColorPlayer,linewidths=0)

  
    # Plotten der Spielmatrix in das Bild
    textstr1=r'$\hat{\bf {\cal \$}} = \left( \begin{array}[c]{cc} a & b \\ \ c & d \end{array} \right)\\a='+str(a)+', b='+str(b)+'$'
    textstr2=r'$\\c='+str(c)+', d='+str(d)+'$'
    ax2.text(4, 0.58, textstr1+textstr2, fontsize=12, verticalalignment='bottom', horizontalalignment='right', bbox=props)
  
    #Jeder Spieler spielt das vorgegebene Spiel mit seinen naechsten Nachbarn, Speichern des gesamten Gewinns/Verlusts in PropPlayers[:,4] und PropPlayers[:,5]
    for games in list(G.edges):
        PropPlayers[games[0]].flat[4] = Dollar(PropPlayers[games[0],3],PropPlayers[games[1],3],a,b,c,d) + PropPlayers[games[0],4] 
        PropPlayers[games[1]].flat[4] = Dollar(PropPlayers[games[1],3],PropPlayers[games[0],3],a,b,c,d) + PropPlayers[games[1],4] 
        PropPlayers[games[0]].flat[5] = Dollar(PropPlayers[games[0],3],PropPlayers[games[1],3],a,b,c,d) + PropPlayers[games[0],5] 
        PropPlayers[games[1]].flat[5] = Dollar(PropPlayers[games[1],3],PropPlayers[games[0],3],a,b,c,d) + PropPlayers[games[1],5] 
    
    #Vergleich der eigenen Auszahlungswerte mit den Nachbarn und Festlegung der zukuenftigen Strategie in PropPlayers[:,6]  
    for kk in range(0,NKn): 
        neighbors = list(G.neighbors(kk))
        neigh_payoffs = PropPlayers[neighbors, 4]
        max_payoff = np.max(neigh_payoffs)
        if max_payoff > PropPlayers[kk,4] and not isclose(max_payoff, PropPlayers[kk,4]):
                best_idx = np.argmax(neigh_payoffs)
                PropPlayers[kk, 5] = PropPlayers[neighbors[best_idx], 4]
                PropPlayers[kk, 6] = PropPlayers[neighbors[best_idx], 3]
#        for neig in neighbors:
#            if PropPlayers[neig,4] > PropPlayers[kk,4] and PropPlayers[neig,4] > PropPlayers[kk,5] and not isclose(PropPlayers[neig,4], PropPlayers[kk,4]) and not isclose(PropPlayers[neig,4], PropPlayers[kk,5]):
#                PropPlayers[kk].flat[5] = PropPlayers[neig,4]
#                PropPlayers[kk].flat[6] = PropPlayers[neig,3]
  
    #Farbenfestlegung der zukuenftigen Strategienwahl  
    ColorPlayernew=[]
    for kk in range(0,NKn): 
        if PropPlayers[kk,6]==0:
            ColorPlayernew.extend("r")
        if PropPlayers[kk,6]==1:
            ColorPlayernew.extend("b")
        PropPlayers[kk].flat[5] = 0
        PropPlayers[kk].flat[4] = 0  
        PropPlayers[kk].flat[3] = PropPlayers[kk,6]

    #Mittlere Strategienwahl der Spielerpopulation
    AveragePropPlayers[it,0] = np.sum(PropPlayers[:,3])/NKn

    #Visualisierung der Auszahlungen der zukuenftigen Strategie als kleines Viereck
    ax3.scatter(PropPlayers[:,1]-0.25,PropPlayers[:,2]-0.25,s=sklein,c=ColorPlayernew,marker="s",alpha=1,edgecolor='none')

    print(it,"---------------------------------------------------")
    #Speicherung des Bildes als .png und .pdf Datei
    saveFig = "./output/img-" + "{:0>3d}".format(it) + ".png"
    plt.savefig(saveFig, dpi=400,bbox_inches="tight",pad_inches=0.05,format="png")
#    saveFig = "./output/img-" + "{:0>3d}".format(it) + ".pdf"
#    plt.savefig(saveFig,bbox_inches="tight",pad_inches=0.05,format="pdf")
#   Die einzelnen Bilder koennen dann in eine Animation zusammengefuegt werden.
#   z.B. mit folgende Shell-Befehl: ffmpeg -r 2 -i img-%03d.png spatialgame.avi
       
    plt.gcf().clear()
