import networkx as nx
import matplotlib.pyplot as plt
from random import randint
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 = 40#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 in [301,302,303,304,333,334,363,364,393,394,475,476,505,506,535,536,565,566,567,568]:
            PropPlayers[k,3] = 1 #Aktuelle Strategienwahl
            PropPlayers[k,6] = 1 #Zukuenftige Strategienwahl
        else:
            PropPlayers[k,3] = 0 #Aktuelle Strategienwahl
            PropPlayers[k,6] = 0 #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): 
        for neig in list(G.neighbors(kk)):
            if PropPlayers[neig,4] > PropPlayers[kk,4] and 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()
