# Hacking the Language of Crowds simulation software
#
# please read the following texts to get the point
# http://socialfiction.org/onlyone/hackcrowd.html
# http://socialfiction.org/crystalpunk.html

# if you think this script seriously means anything you are insane
# loose ends in code might be possible but can't be bothered to check.

# what is it if its not a proper simulation?
# an aid for reflection

# written in Python, you need to have python 2.3 or up and Tkinter installed to run
# download python for free at http://python.org

# have fun!

# 21 Feb 2005 socialfiction.org


from Tkinter import *
import random
import string

seq=[5,-5,0,0]
crowd=[] # crowd
crystal=[] # crysalline atoms
direction=[] # direction crowd 
tekst=[] # trace
tracerstatus=["hide"]
crystalstatus=["hide"]
copycrystal=[]
moveski=["hide"]

def help():
    string1="Hacking the Language of Crowds\nA Crystal-Punk project by socialfiction.org"
    stringt="\n\n"+"-"*180+"\n"
    string2= "Men travel in manifold paths: who traces and compares these, will find strange\
Figures come to light; Figures which seem as if they belonged to that great Cipher-writing\"\
- Novalis\n\nThere exists a steady theoretical tradition (Gustave le Bon, Elias Canetti)\
of looking at crowds as the self-fulfilling prophecy of a meta-species. As autonomous entities,  independent from the intentions of the individuals it is made of, consciously acting in the world. When looking for factual prove of the existence of such a proactive, instead of floral-like adaptive, intelligence, one can only start by assuming that this entity must produce some observable patterns. Patterns we can isolate and decode.\n\nThe search for urban crowd intelligence will be undertaken by approaching the crowd as a medium in which particles can be dissolved and from which meaning can be extracted by analysing the self-assembly of these particles into Crowd Crystals.\n\nThe Crowd Crystal are formed in response to, and are the result of, crowd agitation. A crystal is a book that tells us about the system that produced it. And no: putting them under your pillow will not cure you from your constipation.\n\nHere we present 2 crowd-crystal simulations as an aid to reflection to help prepare experiments with real crowds and real agents. One simulation is based on the study of trajectories, the other aims at understanding crowd-crystal semiotics."

    stringtraject="Crowd Crystal Simulation [trajectories]\n\nSTART: The crowd (the circles) are\
randomly assigned a position and a direction. The crystalline agents (the black rectangles)\
too are assigned a random position, they however only move when at least 3 crowd-members are\
within the boundaries of its proximity and it moves to follow.\n\nRUN: The behaviour of\
the crystalline agents can be altered by widening or narrowing the space it recognises the\
crowd. This is done with the CRYSTAL DEPTH slider. The CRYSTAL DEPTH  button draws the\
current detection field for each crystalline agent.\n\nANALYSIS: By using the TRACE button\
the movement of the crystalline agents over time are shown. In combination with the ABS. MOVEMENT\
button traces can be compared with the original position of the agent. Use your Novalis-instinct \
to make sense of this and leave feedback when interesting patterns occur.\n\n"


    def stop():
        r.destroy()
        r.quit()



    r=Tk()
    r.config(bg="limegreen")
    r.overrideredirect(1)
    t=Text(r,width=90,height=40, bg="grey", wrap=WORD)
    t.grid(column=0, row=0)
    t.insert(END,string1)
    t.insert(END,stringt)
    t.insert(END,string2)
    t.insert(END,stringt)
    t.insert(END,stringtraject)
    b=Button(r,text="close", command=stop)
    b.grid(column=0, row=1)
    r.mainloop()



def start(): # def crowd
    for i in range(0,250):
        x=random.randint(0,70)*10
        y=random.randint(0,45)*10
        d1=random.choice(seq)
        d2=random.choice(seq)
        if d1==0 and d2==0: d2=random.choice(seq)
        crowd.append([x,y])
        direction.append([d1,d2])
        c.create_arc(x,y,x,y,start=0,extent=359, fill="white", style="chord")
        

def atoms(): # def crystal
    for i in range(0,120):
        x=random.randint(1,70)*10
        y=random.randint(1,45)*10
        crystal.append([x,y])
        c.create_rectangle([x,y,x+5,y+5], fill="black")
        copycrystal.append([x,y])


def drawcryst():
    tekstt=string.join(tekst)
    leeg=Label(root,text=tekstt[-10:])
    leeg.grid(row=2,columnspan=5)
    for x in range(len(crystal)):
        c.create_rectangle(crystal[x][0]-2,crystal[x][1]-2,crystal[x][0]+2,crystal[x][1]+2, fill="red")
            
def abmove():
    for i in range(len(crystal)):
        c.create_line(copycrystal[i][0],copycrystal[i][1],crystal[i][0],crystal[i][1], fill="lightblue")

def walk():
    c.delete("all")
    for x in range(len(crowd)):
        pedestrian=crowd[x]
        pedestrian[0]=pedestrian[0]+direction[x][0]
        pedestrian[1]=pedestrian[1]+direction[x][1]
        if pedestrian[0]>700:direction[x][0]=-1
        if pedestrian[0]<0:direction[x][0]=1
        if pedestrian[1]>450:direction[x][1]=-1
        if pedestrian[1]<0:direction[x][1]=1
        c.create_arc([pedestrian[0],pedestrian[1],pedestrian[0]+3,pedestrian[1]+3],start=0,extent=359, fill="white", style="chord")


def atomwalk():
    if tracerstatus[0]=="show": trace()
    if moveski[0]=="show": abmove()
    for i in range(len(crystal)):
        prox=[]
        x=crystal[i][0]
        y=crystal[i][1]
        ss=sl.get()
        # determine for each crystal the atoms in proximity
        for ii in range(len(crowd)):
            xx=crowd[ii][0]
            yy=crowd[ii][1]
            
            if x<(xx+ss) and x>(xx-ss):
                if yy<(y+ss) and yy>(y-ss):
                    prox.append((xx,yy))

                
        #when enough atoms in prox: move
        if len(prox)>2:
            if (x,y) not in tekst: tekst.append((x,y))
            d1=d2=0
            for ii in range(len(prox)):
                d1=d1+prox[ii][0]
                d2=d2+prox[ii][1]
            if x>(d1/len(prox)):
                if ([crystal[i][0]-5,crystal[i][1]]) not in crystal:
                    crystal[i][0]=crystal[i][0]-5
            elif x<(d1/len(prox)):
                if ([crystal[i][0]+5,crystal[i][1]]) not in crystal:
                    crystal[i][0]=crystal[i][0]+5
            elif x==(d1/len(prox)): pass
            if y>(d2/len(prox)):
                if ([crystal[i][0],crystal[i][1]-5]) not in crystal:
                    crystal[i][1]=crystal[i][1]-5
            elif y<(d2/len(prox)):
                if ([crystal[i][0],crystal[i][1]+5]) not in crystal:
                    crystal[i][1]=crystal[i][1]+5
            elif y==(d2/len(prox)): pass      
        
        if crystalstatus[0]=="show":
            c.create_rectangle(x+ss,y-ss,x-ss,y+ss, outline="lightgrey")
        c.create_rectangle(x-2,y-2,x+2,y+2, fill="black")



    
        
def honderd():
    c.delete("all")
    del crowd[:]; del crystal[:]; del direction[:]; del tekst[:]; del copycrystal[:]
    start()
    atoms()
    while 1==1:
        doe()
        


def doe():
    walk()
    atomwalk()
    root.update()



def trace():
    i=0
    if len(tekst)>1200:
        tekst[:]=tekst[(len(tekst)-1200):] 
    while i<len(tekst):
        x=tekst[i][0]
        y=tekst[i][1]
        c.create_rectangle(x,y,x+1,y+1,outline="limegreen")
        i=i+1
        
        


def crystall():
    for i in range(0,(len(crystal)-1)):
        c.create_line(crystal[i][0],crystal[i][1],crystal[i+1][0],crystal[i+1][1])
        c.create_text(crystal[i][0]+5,crystal[i][1]+5,text=i+1)

def showtracer(): tracerstatus[0]="show"
def hidetracer(): tracerstatus[0]="hide"
def showdepth(): crystalstatus[0]="show"
def hidedepth(): crystalstatus[0]="hide"
def am(): moveski[0]="show"
def am1(): moveski[0]="hide"

def override(event):root.overrideredirect(1)
def override1(event):root.overrideredirect(0)

root=Tk()
root.title("Crowd Cystal simulation [trajectory]")
root.config(width=600,height=600,bg="white")
#root.overrideredirect(1)
c=Canvas(root, width=700, height=450, background="white")
c.grid(row=0, columnspan=10)
honderd=Button(root, text="Start new Simulation", command=honderd)
honderd.grid(row=1,column=0)


leeg1=Label(text=" crystal depth:", bg="limegreen")
leeg1.grid(row=1,column=1,sticky=E+W)
sl=Scale(orient=HORIZONTAL, from_=15, to=200, activebackground="white", bg="white", showvalue=NO, highlightbackground="white")
sl.grid(row=1,column=2)
sl.set(30)

leegx=Label(root,text="  abs. movement: ", bg="limegreen")
leegx.grid(row=2,column=1)
qa1=IntVar()
tracer4=Radiobutton(text="show", variable=qa1, value=1, command=am, bg="white")
tracer4.grid(row=2,column=2, sticky=W)
tracer14=Radiobutton(text="hide", variable=qa1, value=2, command=am1, bg="white")
tracer14.grid(row=2,column=2, sticky=E)
tracer14.select()


leeg=Label(root,text="  trace: ", bg="limegreen")
leeg.grid(row=2,column=4)
q1=IntVar()
tracer=Radiobutton(text="show", variable=q1, value=1, command=showtracer, bg="white")
tracer.grid(row=2,column=5)
tracer1=Radiobutton(text="hide", variable=q1, value=2, command=hidetracer, bg="white")
tracer1.grid(row=2,column=6)
tracer1.select()

leeg2=Label(root,text=" crystal depth: ", bg="limegreen")
leeg2.grid(row=1,column=4)
a1=IntVar()
tracerq=Radiobutton(text="show", variable=a1, value=1, command=showdepth, bg="white")
tracerq.grid(row=1,column=5)
tracerqq=Radiobutton(text="hide", variable=a1, value=2, command=hidedepth, bg="white")
tracerqq.grid(row=1,column=6,)
tracerqq.select()
helpp=Button(text="help", bg="limegreen", command=help).grid(row=1,column=8)
root.bind("<F6>", override)
root.bind("<F7>", override1)

root.mainloop()
