snake.py

Created by stimorol

Created on November 19, 2022

6 KB

Intelligence artificielle utilisant le machine learning. Nécessite la bibliothèque numpy, uniquement accessible avec Upsilon. Il est aussi possible de sauvegarder et charger les meilleurs cerveaux.


from time import *
from ulab import numpy as np
from random import *
from kandinsky import fill_rect as rect, draw_line as line, draw_string as txt
from ion import keydown as key

# MADE BY ARMAND JAMET
# STARTED 10.11.22, ENDED 16.11.22

rate=lambda step,fdEaten: fdEaten-step/15
r=lambda x,i:str(x)[0:i]
saveName='brain.ia'
def save(name,brain):
  print('Saving...')
  with open(name,'w') as f:
    f.truncate(0)
    sav=''
    for i in [0,1]:
      for j in range(len(brain[i])):
        sav+=':'.join([r(k,4) for k in brain[i][j]])
        sav+=';'
      sav+='/'
    f.write(sav)
    del sav
def load(name,sav='0'):
  print('LOADING...')
  with open(name,'r') as f:
    line,brain,out=1,[],[[],]*4
    while line!='':
      line=f.readline()
      sav+=str(line)
    out[0]=sav.split('/')
    for i in [0,1]:
      out[1]=out[0][i].split(';')# 4 lists of 8 str
      out[2]=[]
      for j in range(4):
        out[3]=out[1][j].split(':')# 1 list of 8 str()
        if out[0]=='0':out[3]=out[3][1:]
        out[2].append(np.array([float(out[3][k]) for k in range(len(out[3]))]))
      brain.append(np.array(out[2]))
    return brain
class Graphics():
  def __init__(slf,pos,fdPos,conf,col):
    slf.conf,slf.col,slf.fdPos=conf,col,fdPos
    slf.drawGrid(conf)
    slf.refresh(pos,1)
  def drawGrid(slf,conf,bg=False):
    rect(0,0,322,222,bg if bg else slf.col[2])
    for x in range(0,(conf[2]+1)*10,10):line(conf[0]+x,conf[1],conf[0]+x,conf[1]+conf[3]*10,slf.col[1])
    for y in range(0,(conf[3]+1)*10,10):line(conf[0],conf[1]+y,conf[0]+conf[2]*10,conf[1]+y,slf.col[1])
    rect(slf.fdPos[0]*10+slf.conf[0]+1,slf.fdPos[1]*10+slf.conf[1]+1,9,9,'green')
  def refresh(slf,pos,rep,fdPos=0):
    if rep:rect(pos[0]*10+slf.conf[0]+1,pos[1]*10+slf.conf[1]+1,9,9,slf.col[0])
    else:rect(pos[0]*10+slf.conf[0]+1,pos[1]*10+slf.conf[1]+1,9,9,slf.col[2])
  def showStats(slf,i,info=['None'],bg=0,x=195,y=70):
    rect(x,y,125,125,slf.col[2])
    for i in range(len(info)):txt(str(info[i]),x,y+i*10,slf.col[1],slf.col[2],1)
  def name(slf,i,col,bg):txt('AGENT '+str(i+1)+': '+['A','B','C','MA','MA1','MB','MB1','MC','MC1','AB','R','R1'][i],185,42,col,bg)
class Game():
  def __init__(slf,anim=1,s=.2,perGen=12,conf=[30,61,10,10],col=['red','gray',(0,0,50)]):
    slf.conf,slf.col,slf.perGen,slf.anim,slf.s,sav=conf,col,perGen,anim,s,0
    if input('Hello, user !\nLoad sav ?\n... ')=='':sav=load(saveName)
    if anim=='in':slf.anim=bool(input('Enable graphics ?\n... ')=='')
    if not sav:slf.ai=Agent(perGen)
    else:slf.ai=Agent(perGen,sav)
    slf.main()
  def newFdPos(slf):
    if slf.anim:
      slf.graph.fdPos=[randint(0,slf.conf[2]-1),randint(0,slf.conf[3]-1)]
      return slf.graph.fdPos
    return [randint(0,slf.conf[2]-1),randint(0,slf.conf[3]-1)]
  def get_input_layer(slf,pos,fdPos):
    return np.array([
    # DIST TO FOOD
    pos[1]-fdPos[1], # UP
    fdPos[1]-pos[1], # DOWN
    pos[0]-fdPos[0], # LEFT
    fdPos[0]-pos[0], # RIGHT
    # DIAGONALS ( IN BIN )
    (pos[1]>fdPos[1] and pos[0]>fdPos[0]), # UL
    (pos[1]>fdPos[1] and pos[0]<fdPos[0]), # UR
    (pos[1]<fdPos[1] and pos[0]<fdPos[0]), # DR
    (pos[1]<fdPos[1] and pos[0]>fdPos[0])]) # DL
  def train(slf,pos,fdPos,i,step=0,fdEaten=0):
    while 0<=pos[0]<slf.conf[2] and 0<=pos[1]<slf.conf[3]:
      inp=slf.get_input_layer(pos,fdPos)
      out=slf.ai.compute(inp,i)
      dir=np.argmax(out)
      if slf.anim:
        slf.graph.refresh(pos,1)
        slf.graph.showStats(i,['IN:     OUT:','']+[str(inp[i])+'  -  '+str(round(out[i],2))+' '+'UDLR'[i] for i in range(4)]+[str(inp[i]) for i in range(4,8)]+['OUT:'+['   UP',' DOWN',' LEFT','RIGHT'][dir],'GRADE:'+r(rate(step,fdEaten),len(str(int(rate(step,fdEaten))))+2)])
        sleep(slf.s)
        slf.graph.refresh(pos,0)
      pos[dir in [0,1]]+=[-1,1][dir%2]
      if key(17) or step>15 or fdEaten>10:break
      step+=1
      if pos[0]==fdPos[0] and pos[1]==fdPos[1]:
        fdEaten+=1
        fdPos,step=slf.newFdPos(),0
        if slf.anim:
          for j in [(0,125,0),slf.col[2]]:
            slf.graph.drawGrid(slf.conf,j)
            slf.graph.refresh(pos,1)
            slf.graph.name(i,(120,160,200),(0,125,0))
            sleep(j==(0,125,0))
    if slf.anim:
      slf.graph.drawGrid(slf.conf,(125,0,0))
      slf.graph.refresh(pos,1)
      sleep(.4)
    return rate(step,fdEaten)
  def trainGen(slf,s,gen):
    grades=[]
    for i in range(slf.perGen):
      seed(s)
      pos=[randint(0,slf.conf[2]-1),randint(0,slf.conf[3]-1)]
      fdPos=[randint(0,slf.conf[2]-1),randint(0,slf.conf[3]-1)]
      if slf.anim:
        slf.graph=Graphics(pos,fdPos,slf.conf,slf.col)
        slf.graph.name(i,(120,160,200),slf.col[2])
      grades.append(slf.train(pos,fdPos,i))
      if key(6):
        slf.anim=not slf.anim
        input('Graphics turned '+['off','on'][slf.anim]+'... ')
    if not slf.anim:print('GEN',gen,': AVG.',int(sum(grades)/len(grades)),'\n → GRADES',', '.join([str(int(i)) for i in grades]))
    if key(4):save(saveName,[slf.ai.brain[0],slf.ai.brain[1]])
    slf.ai.evolve(grades)
  def main(slf):
    print('   ****   START OF SIMULATION   ****')
    for i in range(100_000):slf.trainGen(int(monotonic()),i)
class Agent():
  def __init__(ai,perGen,sav=[],learningRate=.5,defaultVal=(-1,1)):
    ai.brain,ai.lRate,ai.dval=[],learningRate,defaultVal
    ai.sigm=lambda x: np.array([1])/(np.exp(-x)+1)-.5
    if sav!=[]:
      ai.brain=sav
      ai.evolve([1,1,1])
    else:
      for i in range(perGen):ai.brain.append(ai.createBrain(defaultVal))
  def createBrain(ai,defaultVal):return np.array([np.array([uniform(defaultVal[0],defaultVal[1]) for _ in range(8)]) for _ in range(4)])
  def compute(ai,input,i):return ai.sigm(np.dot(ai.brain[i],input))
  def evolve(ai,grades):
    best=[]
    for _ in range(3):
      best.append(ai.brain[grades.index(max(grades))])
      grades.remove(max(grades))
    ai.brain=best
    del best
    for i in [0,1,2]:
      for j in [0,1]:ai.brain.append(ai.brain[i]+ai.createBrain([-ai.lRate,ai.lRate]))
    for _ in range(2):ai.brain+=[ai.createBrain(ai.dval)]
    ai.brain.append((ai.brain[0]+ai.brain[1])/2)

game=Game('in',0.2)

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.