#!/usr/bin/python

# machine code for the genome
# | -- set flow left if [DP] is even, right if it is odd
# ? -- skip next instruction if [DP] is even [operates backwards if stepping left]
# ! -- skip next instruction if [DP] is odd [operates backwards if stepping left]
# < -- decrement DP
# > -- increment DP
# + -- increment [DP]
# - -- decrement [DP]
# execution flows from left to right, then loops to 0

# next we implement brainfuck and do it in that ok :D
# i think , should come from /dev/urandom and . should be used to
# test if they are printing flamoot

import random, time, sys
import os

popsize = 25
#maxlen = 107 # try xterm sized 121x31
maxlen = 130
winstring = "0xFLAM00T" # set to preferred string of length < datalen
#winstring = "AAAAAAAAA" # easy
#winstring = "0xDEADBEEF" # eh
#winstring = "/etc/etc/etc"# etc
datalen = 33
#maxations = 5000 # number of cycles per generation
maxations = 2500

print

opcodes = ["|","?","!","<",">","+","-"]
datacodes = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_,.!:=-/"

t = list(datacodes)
#random.shuffle(t)
datacodes = "".join(t)

oddnesstable = []
odds = 0
evens = 0
for dc in datacodes:
  oddnesstable.append(ord(dc) % 2)
  if oddnesstable[-1] == 1:
    odds += 1
    print dc+" is odd\t",
  else:
    evens += 1
    print dc+" is even\t",

print "\n"+str(odds) + " odd datacodes available"
print str(evens) + " even datacodes available"

time.sleep(1)

print "Initializing population"
population = []

for k in xrange(popsize):
  specimen = ""
  for l in xrange(maxlen):
    specimen += random.choice(opcodes)
  population.append(specimen)

#sys.exit(1)

IPs = [0] * popsize
DPs = [0] * popsize
datas = [" "*datalen] * popsize
flows = [1] * popsize # flow of -1 is reverse
scores = [" ,1"] * popsize

#print datas

def tobinary(n):
  a = 256
  tn = n
  bin = ""
  while a > 1:
    a = a / 2
    if tn - a > -1:
      tn -= a
      bin += "1"
    else:
      bin += "0"
  return bin

def fhex(string):
  foo = "0123456789abcdef"
  b = foo.index(string[0].lower())
  s = foo.index(string[1].lower())
  return int(b) * 16 + int(s)

def calcentropy(one,two,receipt=1):
  pair = one+two
  one = tobinary(ord(pair[0]))
  two = tobinary(ord(pair[1]))
  if receipt > 0:
    print "["+one+"] -> ["+two+"]",
  entropy = 0.0
  for t in xrange(8):
    if one[t] != two[t]:
      entropy += 1.0
  entropy = entropy / 8.0
  if receipt > 0:
    print "("+str(entropy)+")"
  return entropy

def avgentropy(key, receipt=1):
  try:
    #key = key.strip().replace("-","").strip()
    if receipt > 0:
      print "\n----"+"-"*len(key)+"----"
      print "vvv "+key+" vvv"
    entrp = 0.0
    for t in xrange(len(key) - 2):
      entrp += calcentropy(key[t],key[t+1],receipt=receipt)
    entrp = entrp / float(len(key) - 1)

    if receipt > 0:
      print "^^^ "+str(entrp).center(len(key))+" ^^^"
  except:
    print "* bork"

  return entrp

def breed(a,b):
  a = list(a)
  b = list(b)  

  print a
  print b

  if len(a) != len(b):
    t = len(a)
    ta = a
    tb = b
    if len(b) < t:
      t = len(b)
      b += random.choice(opcodes)*(len(a)-len(b))
    else:
      t = len(a)
      a += random.choice(opcodes)*(len(a)-len(b))
    newlen = t + abs((len(ta)-len(tb))/2)
  else:
    newlen = len(a)

  new = ""
  myctr = 0
  mutateorbreed = random.choice([0,1])
  for gene in b:
#    print datacodes
#    print gene
#    print b
    if mutateorbreed==1:
      new += opcodes[(([opcodes.index(gene),opcodes.index(a[myctr])][mutateorbreed]) + random.choice([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,8,8,9,10,20,40,80,160])) % len(opcodes)]
    else:
      if random.choice([1,2])==1:
        new += opcodes[((opcodes.index(gene)) + random.choice([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,8,8,9,10,20,40,80,160])) % len(opcodes)]
      else:
        new += opcodes[((opcodes.index(a[myctr])) + random.choice([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,8,8,9,10,20,40,80,160])) % len(opcodes)]
      # hmm this is the same thing i just did. breeding was broken before sorry about that
          
    myctr += 1

  return new

top = " ,-100000000.0"
one = " ,-100000000.0"
two = " ,-100000000.0"
entropy = 0
while (1):
  ations = 0
  IPs = [0] * popsize
  DPs = [0] * popsize
  datas = [" "*datalen] * popsize
  flows = [1] * popsize # flow of -1 is reverse
  scores = [" ,1"] * popsize

  while(ations < maxations):
    specimenctr = 0

#    time.sleep(.01)
    sys.stdout.flush()
    print "\33[H\33[J"
    print str(ations)+" / "+str(maxations)+"        top score "+str(top.split(",")[1])+"\t\tw/ entropy parity "+str(entropy)+"%"
    for specimen in population:
      score = int(scores[specimenctr].split(",")[1])
      score += 1
      scores[specimenctr] = specimen+","+str(score)

      IP = IPs[specimenctr]
      opcode = specimen[IP]
      data = datas[specimenctr]
      DP = DPs[specimenctr]
      print specimen+"    "+data
      print (" "*IP) + "^" + (" "*((maxlen-IP)-1)),
      sys.stdout.flush()
      print "  ",
      print (" "*DP) + opcode
      sys.stdout.flush()
      #print DP
      #print data[DP]
      #print datacodes.index(data[DP])
      if opcode == "|":
        if oddnesstable[datacodes.index(data[DP])] == 1:
          IP -= 1
          flows[specimenctr] = -1
        else:
          IP += 1
          flows[specimenctr] = 1
      elif opcode == "?":
        if oddnesstable[datacodes.index(data[DP])] == 1:
          IP += flows[specimenctr]
        else:
          IP += flows[specimenctr]*2
      elif opcode == "!":
        if oddnesstable[datacodes.index(data[DP])] == 1:
          IP += flows[specimenctr]*2
        else:
          IP += flows[specimenctr]
      elif opcode == "<":
        DP -= 1
        #      if DP < 0:
        #        DP = datalen#-1
        #      elif DP > datalen:
        #        DP = 0
        IP += flows[specimenctr]
      elif opcode == ">":
        DP += 1
        #      if DP < 0:
        #        DP = datalen#-1
        #      elif DP > datalen:
        #        DP = 0
        IP += flows[specimenctr]
      elif opcode == "+":
        dl = list(data)
        dl[DP] = datacodes[(datacodes.index(data[DP])+1) % len(datacodes)]
        if winstring.__contains__(dl[DP]):
          score = int(scores[specimenctr].split(",")[1])
          score += 1
          scores[specimenctr] = specimen+","+str(score)
          if winstring.startswith(dl[DP]):
            score = int(scores[specimenctr].split(",")[1])
            score += 2
            scores[specimenctr] = specimen+","+str(score)
#          print score
        data = ''.join(dl)
        datas[specimenctr] = data
        IP += flows[specimenctr]
      elif opcode == "-":
        dl = list(data)
        dl[DP] = datacodes[(datacodes.index(data[DP])-1) % len(datacodes)]
        if winstring.__contains__(dl[DP]):
          score = int(scores[specimenctr].split(",")[1])
          score += 1
          scores[specimenctr] = specimen+","+str(score)
          if winstring.startswith(dl[DP]):
            score = int(scores[specimenctr].split(",")[1])
            score += 2
            scores[specimenctr] = specimen+","+str(score)
#          print score
        data = ''.join(dl)
        datas[specimenctr] = data
        IP += flows[specimenctr]
#      try:
#        print score # undeclared here
#      except:
#        pass
      IPs[specimenctr] = IP % len(specimen)
      DPs[specimenctr] = DP % datalen
      specimenctr += 1
    
    
    specimenctr = 0
    for specimen in population:
      data = datas[specimenctr]

      if ations >= maxations-(maxations/5):
        score = int(scores[specimenctr].split(",")[1])
#       print score

        if data.__contains__(winstring[0:1]):
          score += 500 * data.count(winstring[0:1])
          #        print score
          #        print data
          #        print specimen
          #        sys.exit(1)
        if data.__contains__(winstring[0:2]):
          score += 3750 * data.count(winstring[0:2])
        if data.__contains__(winstring[0:3]):
          score += 15000 * data.count(winstring[0:3])
        if data.__contains__(winstring[0:4]):
          score += 30000 * data.count(winstring[0:4])
        if data.__contains__(winstring[0:5]):
          score += 60000 * data.count(winstring[0:5])
        if data.__contains__(winstring[0:6]):
          score += 90000 * data.count(winstring[0:6])
        if data.__contains__(winstring[0:7]):
          score += 500000 * data.count(winstring[0:7])
        if data.__contains__(winstring[0:8]):
          score += 500000 * data.count(winstring[0:8])
        if data.__contains__(winstring):
          score += 2500000  * data.count(winstring)
        scores[specimenctr] = specimen+","+str(score)
        specimenctr+=1
    # er end for

    ations += 1
    print str(ations)+" / "+str(maxations)+"        top score "+str(top.split(",")[1])+"\t\tentropy "+str(entropy),
  # end while  
  specimenctr = 0
  print scores
#  sys.exit()
  for n in scores:
    t = n.split(",")[0]
    t2 = float(n.split(",")[1])

    # changed this too. yak. works a lot better. great really. entropy measurement is good for this.
    entropy = 1.0 - float(abs(avgentropy(datas[specimenctr]) - avgentropy(winstring)))
    print "entropic salience "+str(entropy*100)+"% out of "+str(t2)
    t2 = t2 * entropy # not:

#    entropy = abs(avgentropy(datas[specimenctr]) * 1000000 - avgentropy(winstring) * 1000000)
#    print "entropy penalty "+str(entropy/4)+"/"+str(t2)
#    t2 -= entropy # not:
#    t2 -= entropy/4
#    sys.exit(1)
      
    n = t+","+str(t2)
    # i dont know what this is supposed to mean
    if t2 > float(one.split(",")[1]) and t2 > float(top.split(",")[1]) and t != one.split(",")[0] and t != two.split(",")[0]:
      two = one
      one = n
      top = n
      print top
      print "print top one"
#      os.system("sleep 3")
    elif t2 > float(two.split(",")[1]) and t != two.split(",")[0] and t != one.split(",")[0]:
      two = n

    specimenctr += 1


    print float(top.split(",")[1])
    print float(n.split(",")[1])
#    if t2 > 0:
#      print "t2 > 0"
#      sys.exit()
#    else:
#      pass
#      os.system("sleep 3")
#christ
      
    if float(n.split(",")[1]) < -100000000.0:
      print float(n.split(",")[1])
      print -100000000.0
      print float(n.split(",")[1])+-100000000.0 
      print (float(n.split(",")[1])>-100000000.0)
      print (float(n.split(",")[1])<-100000000.0)      
      print float(top.split(",")[1])
      print float(n.split(",")[1])+float(top.split(",")[1]) 
      print (float(n.split(",")[1])>float(top.split(",")[1]))
      print (float(n.split(",")[1])<float(top.split(",")[1]))      

      sys.exit()

#    if t2 > float(top.split(",")[1]):
#      print "wtf"
#      sys.exit()

#      top = n

#    try:
#      if t2 > float(two.split(",")[1]):
#        two = n
#    except:
#      print "\n"
#      print repr(n)
#      print "\n"
#      print repr(two)
#      print "\n"
#      sys.exit()

#    if t2 > float(one.split(",")[1]):
#      one = n
      
#      print top
#      print "print top two"
#      os.system("sleep 3")
#      print top
#      print n
#      print "wtf"
#      sys.exit()
      

#  if float(top.split(",")[1]) > float(one.split(",")[1]):
#    two = one
#    one = top
#    print "replacing one and two"
#  else:
#    top = one
#    print "replacing top" #hm


  print top
  print "print top three"
#  os.system('sleep 3')

  entropy = (1.0-abs(avgentropy(top.split(",")[0]) - avgentropy(winstring)))*100.0
  
  if two == " ,-100000000.0":
#    if random.choice([0,1])==1 and one != " ,1" and two.split(",")[0] != top.split(",")[0]:    
#      two = breed(top.split(",")[0],one.split(",")[0])
#    else:
      two = top

  print top
  print "print top four"
#  os.system('sleep 3')


  if one == " ,-100000000.0":
#    if random.choice([0,1])==1 and two != " ,1" and one.split(",")[0] != top.split(",")[0]:
#      one = breed(top.split(",")[0],two.split(",")[0])
#    else:
      one = top

  print top
  print "print top five"
#  os.system('sleep 3')

  if one.split(",")[0] == two.split(",")[0]:
    print "bork??"
    print scores
    print n
    print one
    print two
    print top
    print "print top six"
    
    sys.exit()
    two = "".join(breed(top.split(",")[0],one.split(",")[0]))+","+two.split(",")[1]
  
  if float(top.split(",")[1])==float(maxations+1):
    print "bork??"
    print scores
    print n
    print one
    print two
    print top
    print "print top seven"
    
    sys.exit()

  
  population = [one.split(",")[0], two.split(",")[0]]
  for n in xrange(popsize-2):
    population.append(breed(one.split(",")[0],two.split(",")[0]))

