tower_defense_optimized.py

Created by fixem

Created on May 14, 2026

11.7 KB

Tower Defense V-2.1 Le code a été optimisé pour s’exécuter le plus vite possible et pour prendre le moins de place possible sur votre calcultrice.

Bon jeu !!


from kandinsky import fill_rect,draw_string,get_pixel,set_pixel
from ion import keydown
from time import monotonic
from random import randint
from math import sqrt
pdb=[25,75,150,450,2500]
lVl=0
Wave=[0,0,0,0]
Perso=0
gr1=(75,)*3;gr2=(175,)*3
ro=(255,0,0);ja=(252,216,0);bc=(0,255,255);gr=(125,)*3;no=(0,)*3;bl=(255,)*3;be=(255,204,115)
enemy=[];tower=[];Epos=[];Tpos=[];wave=0;money=50
path1=[(0,42,100,10,be),(90,52,10,80,be),(10,122,80,10,be),(10,132,10,60,be),(10,192,140,10,be),(150,62,10,140,be),(160,62,120,10,be),(280,62,10,120,be),(290,172,30,10,be)]
reset=True
alive=0
def draw(o):
  for i in o:fill_rect(i[0],i[1],i[2],i[3],i[4])
def Info(ready,wave,money):
  mo="" if money>10**999 else money
  if ready:draw_string("Wave:"+str(wave)+"-In progress",0,1,no,gr)
  else:draw_string("Wave:"+str(wave)+" "*12,0,1,no,gr)
  draw_string("Money:"+str(mo),260-10*len(str(mo)),1,ja,gr);fill_rect(0,21,320,1,bl)
def cursor(x,y,c):
  c2=get_pixel(x+2,y)
  fill_rect(x,y,10,1,c);fill_rect(x,y,1,10,c);fill_rect(x+9,y,1,10,c);fill_rect(x,y+9,10,1,c)
  fill_rect(x+2,y,6,1,c2);fill_rect(x,y+2,1,6,c2);fill_rect(x+9,y+2,1,6,c2);fill_rect(x+2,y+9,6,1,c2)
def init(ready,wave,money):
  fill_rect(0,0,320,222,gr);Info(ready,wave,money);draw(path1)
  for t in tower:t.Draw()
E=[(10,.5,3),(50,.5,15),(5,1,1),(3,2,2)]
turn={(92,172,252,322,462,592,722,832)[i]:(2,0,2,3,1,3,2,3)[i] for i in range(8)}
# turn as dict for O(1) lookup
turn_keys=set(turn)
class Enemy:
  __slots__=('dam','n','ID','Lvl','HP','V','R','Tr','D','Ef')
  def __init__(sf,n,lvl):
    sf.dam=0;sf.n=n;sf.ID=len(enemy);sf.Lvl=lvl
    sf.HP=int(E[n][0]*(1+2.5*lvl));sf.V=E[n][1];sf.R=int(E[n][2]*2*(lvl+1))
    sf.Tr=-10*(sf.ID+1);sf.D=3;sf.Ef=0
  def Move(sf,Epos):
    ce=[(0,150,0),(50,)*3,(100,200,100),(0,150,255)]
    if sf.dam>0:c=ro;sf.dam-=1
    else:c=ce[sf.n]
    v=sf.V
    if sf.Tr in turn_keys:sf.D=turn[sf.Tr]
    ex,ey=Epos[sf.ID]
    fill_rect(int(ex),int(ey),6,6,be)
    d=sf.D
    if d==0:ex-=v
    elif d==1:ey-=v
    elif d==2:ey+=v
    else:ex+=v
    Epos[sf.ID]=(ex,ey)
    fill_rect(int(ex),int(ey),6,6,c);sf.Tr+=v
T=[
[(50,2,25,2),(100,3,25,2),(200,3,17,2.25),(400,3,17,2.5),(800,5,17,2.5),(1600,10,15,2.5),(3200,12,12,2.5),("--",15,12,3),("--",)*4],
[(150,10,150,3),(300,20,150,3),(600,50,150,4),(1200,100,150,4),(2400,100,75,5),(4800,150,75,5),(9600,150,65,5),("--",200,65,5),("--",)*4],
[(250,1,5,1.5),(500,2,5,1.5),(1000,2,3,1.5),(2000,3,3,1.5),(4000,3,3,1.75),(8000,4,3,2),(16000,5,3,2),("--",5,2,2.25),("--",)*4],
[(900,2,10,1.5),(1800,3,10,1.5),(3600,3,7,2.5),(7200,4,7,2.5),(14400,5,7,3),(28800,6,7,3),(47600,6,7,3.5),("--",6,5,3.5),("--",)*4],
[(5000,10**100,1000,5),(10000,10**100,950,5.1),(20000,10**100,900,5.2),(40000,10**100,800,5.5),(80000,10**100,700,5.75),(160000,10**100,600,6),(320000,10**100,500,6),("--",10**100,500,10),("--",)*4]]
name=["Soldat","Sniper","Lance-flamme","Minigunner","Anihilateur"]
class Tower:
  __slots__=('ID','n','Lvl','Stats','T')
  def __init__(sf,n,lvl=0):sf.ID=len(tower);sf.n=n;sf.Lvl=lvl;sf.Stats=T[n][lvl];sf.T=0
  def Draw(sf):
    ct=[(255,255,0),(255,0,255),(0,255,255),(255,255,255),(0,0,0)]
    x,y=Tpos[sf.ID];fill_rect(x+2,y+2,6,6,ct[sf.n])
  def Attack(sf,Tpos,enemy,Epos,ready,wave):
    global reset,alive,money
    if reset:reset=False;sf.T=0
    st=sf.Stats;dM=0;cible=None
    x,y=Tpos[sf.ID];cx=x+5;cy=y+5
    r2=(st[3]*10)**2
    for i in range(len(Epos)):
      if enemy[i].HP>0 and enemy[i].Tr>dM:
        ex,ey=Epos[i];dx=cx-ex-3;dy=cy-ey-3
        if dx*dx+dy*dy<=r2:dM=enemy[i].Tr;cible=i
    if cible is not None and sf.T>=st[2]:
      sf.T=0
      e=enemy[cible]
      e.dam=5;e.HP-=st[1]
      if e.HP<=0:
        ID=e.ID;money+=e.R;alive-=1;Info(ready,wave,money)
        ex,ey=Epos[ID];fill_rect(int(ex),int(ey),6,6,be)
        del enemy[cible];del Epos[ID]
        for e2 in enemy:
          if e2.ID>ID:e2.ID-=1
    else:sf.T+=1
def Remplir(Wave=[0,0,0,0]):
  global alive,reset,Perso,Epos,enemy,lVl,wave
  reset=True
  if Perso:Perso=0
  else:
    wav=wave-20*(wave//20);lVl=wave//20
    if wav==0:Wave=[0,0,2,0]
    elif wav==1:Wave=[0,0,0,2]
    elif wav==2:Wave=[2,0,0,0]
    else:Wave=[wav//4+lVl//3,wav//10+wave//50,wav//2+lVl//3,wav//2-1+lVl//3]
  j1=0
  for j,i in enumerate(Wave):
    for _ in range(i):
      alive+=1;j1+=1
      Epos.append((-10*j1,44))
      enemy.append(Enemy(j,lVl))
def cercle(x0,y0,r,c1,e):
  r2=r*r
  for i in range(e):
    ri=r-i;ri2=ri*ri
    xd=x0-int(ri/1.4142)
    xf=x0+int(ri/1.4142)+1
    for x in range(xd,xf):
      dx=x-x0;dy=int(sqrt(ri2-dx*dx))
      x1=x;y1=y0+dy
      for _ in range(4):
        set_pixel(x1,y1,c1)
        x1,y1=x0+y1-y0,y0+x0-x1
def Info2(x,y):
  if (x,y) not in Tpos:return bc
  ID=Tpos.index((x,y));t=None
  for t2 in tower:
    if t2.ID==ID:t=t2;break
  if t is None:return bc
  st=t.Stats;d1=st[1];d2=T[t.n][t.Lvl+1][1]
  if d1>10000:d1=d2=""
  draw_string(name[t.n]+"/Level: "+str(t.Lvl),0,100)
  draw_string("Upgrade: "+str(st[0]),0,120)
  draw_string("Damage: "+str(d1)+"-->"+str(d2),0,140)
  draw_string("Reload delay: "+str(st[2])+"-->"+str(T[t.n][t.Lvl+1][2]),0,160)
  draw_string("Range: "+str(st[3])+"-->"+str(T[t.n][t.Lvl+1][3]),0,180)
  return ro
def Play(tm=0,Wave=[0,0,0,0]):
  global wave,Perso,alive,money,Tpos,tower,enemy,Epos
  money=10**1000 if tm else 50
  Perso=1 if tm else 0
  eff=efff=rd=0;x=40;y=42;ready=0;PV=100;can_place=0
  pt={42:0,43:1,44:2,36:3,37:4};prt={42:25,43:75,44:125,36:450,37:2500}
  ts=1;de=[-10,-10,10,10]
  wave=0;tower=[];Epos=[];Tpos=[];enemy=[]
  init(ready,wave,money)
  co=get_pixel(x,y);cursor(x,y,ro)
  path_color=get_pixel(0,42)
  while PV>0:
    fps=int(1/ts)
    T0=monotonic()
    if keydown(14):
      while keydown(14):draw_string(str(PV)+" PV",290-10*len(str(PV)),202,bl,gr)
    elif keydown(12):
      while keydown(12):draw_string(str(fps)+" fps",280-10*len(str(fps)),202,bl,gr)
    elif keydown(13) and (x,y) in Tpos:
      while keydown(13):col=Info2(x,y)
      init(ready,wave,money);cursor(x,y,col)
    rd+=1
    if rd==5:
      rd=0
      for k in (0,1,2,3,52):
        if keydown(k):
          if k==52 and not ready:
            ready=1;Info(ready,wave,money)
            if Perso:Remplir(Wave)
            else:Remplir()
          cursor(x,y,co)
          if k in (0,3):x+=de[k]
          elif k in (1,2):y+=de[k]
          if x>300:x=0
          elif x<0:x=310
          if y>202:y=22
          elif y<22:y=212
          co=get_pixel(x,y)
          can_place=0 if co==path_color or len(tower)>=30 or (x,y) in Tpos else 1
          cursor(x,y,bc) if can_place else cursor(x,y,ro)
          efff=1
          while keydown(k):pass
      if (x,y) in Tpos:
        ID=Tpos.index((x,y));to=None
        for t2 in tower:
          if t2.ID==ID:to=t2;break
        if to:
          eff=1;ra=to.Stats[3];cercle(x+5,y+5,ra*10,ro,1)
          if keydown(4) and to.Lvl<7:
            if money>=to.Stats[0]:to.Lvl+=1;money-=to.Stats[0];to.Stats=T[to.n][to.Lvl];Info(ready,wave,money)
            while keydown(4):pass
          elif keydown(17):
            a=to.ID;money+=int(3*((to.Lvl+1)*(to.n+1))**2.5)
            tower.remove(to);Tpos.remove((x,y))
            for t2 in tower:
              if t2.ID>a:t2.ID-=1
            fill_rect(x,y,10,10,gr)
      elif eff:init(ready,wave,money);eff=0;cursor(x,y,bc) if can_place else cursor(x,y,ro)
      if efff and eff:init(ready,wave,money);efff=0;cursor(x,y,ro)
    if keydown(48):input();init(ready,wave,money)
    if can_place:
      for k in (42,43,44,36,37):
        if keydown(k):
          if money>=prt[k]:
            tower.append(Tower(pt[k]));Tpos.append((x,y));money-=prt[k]
            for t2 in tower:t2.Draw()
          else:draw_string("Money:"+str(money),260-10*len(str(money)),1,ro,gr)
          can_place=0 if co==path_color or len(tower)>=30 or (x,y) in Tpos else 1
          cursor(x,y,bc) if can_place else cursor(x,y,ro)
    for t2 in tower:t2.Attack(Tpos,enemy,Epos,ready,wave)
    for e in enemy[:]:
      e.Move(Epos)
      ex=Epos[e.ID][0]
      if ex==322:
        PV-=e.HP;e.HP=0;alive-=1
      if e.Ef:fill_rect(int(ex),int(Epos[e.ID][1]),6,6,be);e.Ef=0
    if alive==0 and ready:
      if tm:
        tower.clear();Tpos.clear();Epos.clear();enemy.clear()
        while not keydown(52):pass
        break
      else:wave+=1;money+=5*wave;ready=0;Epos.clear();enemy.clear();Info(ready,wave,money)
    ts=monotonic()-T0
def Help():
  tp=1;col=[0,255,255]
  fill_rect(0,0,320,222,gr)
  pages=[
    "Tower Defense\nPar FIXEM  Version: 2.0",
    "[shift]->FPS  [alpha]->Stats tour\n[cut]->PV  [OK]->Ameliorer\nFleches->Curseur  [EXE]->Vague\n1-5->Placer tours  [del]->Vendre",
    "1->Soldat(25)  2->Sniper(75)\n3->Lance-Flamme(125)\n4->Minigunner(450)\n5->Anihilateur(2500)"
  ]
  for pg in pages:
    fill_rect(0,0,320,222,gr)
    while not keydown(4):
      if col[2]>150 and tp==1:col[2]-=1
      else:
        col[2]+=1
        tp=0 if col[2]<255 else 1
      draw_string(pg,2,60,bl,gr)
      draw_string("[OK] pour continuer",65,202,col,gr)
    while keydown(4):pass
  fill_rect(0,0,320,222,gr)
def Train():
  Wave=Choose();Play(1,Wave)
def Choose():
  global lVl,Perso
  Wave=[0,0,0,0];Perso=1;pos=0
  ne=["Lent: ","Boss: ","Normal: ","Rapide: ","Niveau: "]
  while not keydown(4):
    for i in range(5):
      draw_string(ne[i],25,2+30*i,gr1,gr)
      tx=str(Wave[i]) if i<4 else str(lVl)
      draw_string(tx,25+10*len(ne[i]),2+30*i,gr1,gr)
    if keydown(1) and pos:draw_string("  ",2,2+30*pos,gr1,gr);pos-=1;
    elif keydown(2) and pos!=3:draw_string("  ",2,2+30*pos,gr1,gr);pos+=1
    while keydown(1) or keydown(2):pass
    if keydown(0) and Wave[pos]:Wave[pos]-=1
    elif keydown(3) and sum(Wave)<40:Wave[pos]+=1
    while keydown(0) or keydown(3):pass
    if keydown(46) and lVl:lVl-=1
    elif keydown(45):lVl+=1
    while keydown(46) or keydown(45):pass
    if keydown(52):Wave=[0,0,0,0];lVl=0
    draw_string("->",2,2+30*pos,gr1,gr)
  return Wave
def Inventory():
  ct=[(255,255,0),(255,0,255),(0,255,255),(255,255,255),(0,0,0)]
  fill_rect(0,0,320,222,gr);pos=0
  inv=[Tower(i) for i in range(5)]
  while True:
    t=inv[pos];t.Stats=T[t.n][t.Lvl]
    st=t.Stats
    s1=st[1] if st[1]<=100000 else ""
    draw_string(name[pos],2,2,gr1,gr)
    draw_string("Niveau: "+str(t.Lvl)+" "*9,12+10*len(name[pos]),2,gr2,gr)
    draw_string("Prix->niv "+str(t.Lvl+1)+": "+str(st[0])+" "*6,2,42,gr2,gr)
    draw_string("Prix base: "+str(pdb[pos])+" "*6,2,62,gr2,gr)
    draw_string("Attaque: "+str(s1)+" "*6,2,82,gr2,gr)
    draw_string("Delai: "+str(st[2])+" "*6,2,102,gr2,gr)
    draw_string("Portee: "+str(st[3])+" "*6,2,122,gr2,gr)
    draw_string("Symbole:",2,152,gr2,gr);fill_rect(92,147,30,30,ct[pos])
    if keydown(0):
      pos=(pos-1)%5
      while keydown(0):pass;fill_rect(0,0,320,222,gr)
    elif keydown(3):
      pos=(pos+1)%5
      while keydown(3):pass;fill_rect(0,0,320,222,gr)
    if keydown(1) and t.Lvl<7:t.Lvl+=1
    elif keydown(2) and t.Lvl:t.Lvl-=1
    while keydown(1) or keydown(2):pass
    if keydown(4):break
def Menu():
  global Perso,Wave
  action=[Play,Train,Inventory,Help]
  txt=["Play","Training","Towers","Help"]
  xy=[(35,22),(15,57),(25,77),(35,97)]
  pos=0
  def info():
    fill_rect(0,0,320,222,gr);fill_rect(5,7,100,120,gr1);fill_rect(10,12,90,110,gr)
    draw_string("TOWER DEFENSE",188,2,gr2,gr);draw_string("V2.1",278,22,gr2,gr);draw_string("Par FIXEM",2,200,gr2,gr)
  info()
  while True:
    if keydown(1):
      pos=(pos-1)%len(xy)
      while keydown(1):pass
    elif keydown(2):
      pos=(pos+1)%len(xy)
      while keydown(2):pass
    for i in range(len(txt)):
      draw_string(txt[i],xy[i][0],xy[i][1],gr1 if pos==i else gr2,gr)
    if keydown(4):
      while keydown(4):pass
      wave=0
      try:action[pos]()
      except KeyboardInterrupt:
        from time import sleep;sleep(0.4)
        tower.clear();Tpos.clear();Epos.clear();enemy.clear();Perso=0;Wave=[0,0,0,0]
      info()
try:Menu()
except KeyboardInterrupt:pass

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.