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.1
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 (alle spielen s=1 (Blau) ausser Knoten 12: s=0 (Rot))
G = nx.Graph()
Nx = 5
Ny = 5
NKn = Nx*Ny #Anzahl der Knoten
Nit = 10 #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
#        strategy = randint(0, 1)#Zufaellige Strategienwahl (0,1)
#        PropPlayers[k,3] = strategy
        if k == 12:
            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

# Erzeugung der Gitterstruktur des Netzwerks
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
    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=1700,c=ColorPlayer,marker="s",alpha=1,edgecolor='none') 
    for kk in range(0,NKn):
        ax3.text(PropPlayers[kk,1],PropPlayers[kk,2], str(int(PropPlayers[kk,0])), fontsize=12, verticalalignment='bottom', horizontalalignment='right', bbox=props) 

    #Erzeugung des Netzwerk-Bildes  
    node_size=150
    node_alpha=0.5
    node_color="red"
    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)
    nx.draw_networkx_edges(G,graph_pos,width=edge_tickness, alpha=edge_alpha,edge_color=edge_color)
    nx.draw_networkx_labels(G, graph_pos,font_size=node_text_size,font_family=text_font)

    # 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],4] = Dollar(PropPlayers[games[0],3],PropPlayers[games[1],3],a,b,c,d) + PropPlayers[games[0],4]
        PropPlayers[games[1],4] = Dollar(PropPlayers[games[1],3],PropPlayers[games[0],3],a,b,c,d) + PropPlayers[games[1],4]
        PropPlayers[games[0],5] = Dollar(PropPlayers[games[0],3],PropPlayers[games[1],3],a,b,c,d) + PropPlayers[games[0],5]
        PropPlayers[games[1],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,5] = PropPlayers[neig,4]
                PropPlayers[kk,6] = PropPlayers[neig,3]
	
    #Visualisierung der Auszahlungen der Spieler
    for kk in range(0,NKn):
        textstr4 = r'$ \$ \!=\!'+str(np.round(PropPlayers[kk,4],2))+'$'
        ax3.text(PropPlayers[kk,1],PropPlayers[kk,2], textstr4, fontsize=8, verticalalignment='top', horizontalalignment='left', bbox=props)
  
    #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,5] = 0
        PropPlayers[kk,4] = 0
        PropPlayers[kk,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=170,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()
