Programmiamo'C' un CROBOT


`` Il prossimo mese di Novembre si svolgerà il Torneo Y2K di Crobots, che proclamerà il robot più forte di sempre e porterà  gloria imperitura al suo autore. Quale migliore occasione dunque per gettarsi nella mischia e iscrivere la propria creatura all' evento?''

Se avete letto l' articolo introduttivo su CROBOTS già saprete di che cosa sto parlando, ma un piccolo ripasso non fa mai male.

Come si 'gioca':

CROBOTS è un compilatore ANSI 'C', non un videogioco interattivo!
Il duello con gli avversari umani è pertanto solo mentale e per giunta a distanza. La sfida infatti consiste nel dotare il nostro alter-ego digitale di routine che gli consentano di sopravvivere nell' arena virtuale dove si troverà a combattere con altri  programmi.

Domanda: ma come si scrive un robot?
Risposta: in linguaggio C, è ovvio.

E se qualcuno non ha la più pallida idea di come si programma in C?
Niente panico. Questo articolo è stato pensato proprio per quanti , nutrendo interesse per questa competizione, sono frenati dal timore reverenziale verso tale linguaggio.
E poi CROBOTS è nato proprio per scopi didattici, quindi siate ottimisti!

Nonostante il manuale del gioco presenti già un paio di esempi, questi non sono né attuali (la crobotica negli ultimi anni ha fatto passi da gigante) né particolarmente semplici per l' utente inesperto, in quanto utilizzano da subito tutte le strutture e le possibilità permesse dal compilatore.
Ho quindi deciso di seguire un percorso interamente italiano, partendo dallo studio di un crobot molto semplice (Erica.r), per arrivare poi, passando attraverso livelli di difficoltà crescente (Arale.r e Jedi.r ), a quello più complesso (Goblin.r).

Ma adesso bando agli indugi e andiamo ad incominciare!

La dotazione iniziale:
Quello che ci serve per iniziare a sviluppare le nostre temibilissime macchine da battaglia è ridotto ai minimi termini: Nonostante il programma abbia requisiti hardware veramente minimali,  per lo sviluppo di un robot competitivo è caldamente consigliato l' impiego di un computer potente, in modo da poter testare a lungo e in tutte le situazioni il comportamento della nostra creazione.
Infatti, dal momento che l' andamento di una singola partita può essere influenzato anche da fattori casuali, è opportuno ripetere gli scontri un numero sufficientemente elevato di volte da garantire significatività statistica ai risultati ottenuti.
Il linguaggio di sviluppo:
Si tratta di un sottoinsieme dell' ANSI C, con alcune aggiunte che consentono un pieno controllo sul comportamento del robot, fornendogli le capacità di muoversi, cercare gli avversari e sparare loro.
Non è presente un pre-compilatore, né è implementata alcuna gestione dei file. In conseguenza di ciò il nostro programma non può avere una memoria. Ogni partita per lui è la prima e l' ultima.
La gestione degli errori lascia a desiderare: nel caso se ne verifichi uno (per esempio una divisione per 0) il compilatore resetta semplicemente tutte le variabili del programma e riinizia ad eseguirlo dalla prima istruzione della procedura principale.
Inoltre è presente un bug nella funzione che restituisce la distanza del nemico: la scan() non vede gli avversari che si trovano nel cono compreso tra 0 e -10 gradi. Questo, unito al fatto che il compilatore considera sempre il valore assoluto degli angoli che gli passiamo, può a volte generare dei problemi .
Ogni robot ha a disposizione 999 VirtuaByte per il codice, e ciascuna istruzione occupa esattamente lo stesso ammontare di memoria. Ciò consente la stesura di programmi di circa 100/120 linee. Questo limite, inizialmente non molto sentito (i primi robot arrivavano a stento ai 100 VB), è oggi l' ostacolo più grande contro cui si infrangono gli sforzi dei croboticisti, ma è anche grazie alla sua presenza che l' ottimizzazione delle routine ha raggiunto livelli incredibili. Solo per fare un esempio osservate l' evoluzione della procedura per il calcolo dell' angolazione necessaria a raggiungere un determinato punto:

Questa è quella fornita nel manuale:

plot_course(xx,yy)                                                          /*Inizia la procedura, passandogli come parametri
                                                                                             le coordinate della destinazione.....*/
int xx, yy;                                                                          /*....che vengono dichiarate esternamente alla
                                                                                              procedura stessa*/
{
  int d;                                                                                /*Dichiara le variabili che userà nella procedura*/
  int x,y;                                                                             /*Poiche' il compilatore e` molto semplificato */
  int scale;                                                                         /*non gestisce i numeri in virgola mobile*/
  int curx, cury;                                                                 /*ma solo gli interi*/

  scale = 100000;                                                              /*quindi le funzioni trigonometriche restituiscono
                                                                                            un risultato compreso tra 1 e 10000 anzichè tra
                                                                                            0 e 1*/
  curx = loc_x();                                                               /*Memorizza in due variabili la posizione
                                                                                             attuale*/
  cury = loc_y();
  x = curx - xx;
  y = cury - yy;

                                                                                          /*Poichè l' arcotangente (atan) restituisce un
                                                                                            valore compreso tra -90 to +90, per ottenere un
                                                                                            risultato utile sui 360 gradi dobbiamo
                                                                                            modificarlo con altre informazioni di cui
                                                                                            disponiamo.........*/

  if (x == 0)                                                                       /*....ed è proprio ciò di cui si occupa questa
                                                                                             parte*/
  {
    if (yy > cury)
      d = 90;
   else
      d = 270;
  }
 else
  {
    if (yy < cury)
     {
      if (xx > curx)
        d = 360 + atan((scale * y) / x);                                /*Ci troviamo nel quadrante di sud-est*/
      else
        d = 180 + atan((scale * y) / x);                                /*Siamo nel quadrante di sud-ovest */
      }
   else
   {
      if (xx > curx)
        d = atan((scale * y) / x);                                          /*Siamo nel quadrante nord-est */
      else
        d = 180 + atan((scale * y) / x);                                /*E questo e` il quadrante nord-ovest*/
    }
  }

Questa era inclusa in un crobot del 1997:

Go(x1,y1)
{
    int curx,nx,ny,dist;                                                        /* Calcola la direzione per recarsi al punto
                                                                                               stabilito: */
    nx=(curx=loc_x())-x1;
    ny=(loc_y()-y1)*100000;
    if (x1>curx) dir=360+atan(ny/nx);
           else dir=180+atan(ny/nx);
  }

E questa infine viene dalla collezione autunno-inverno 1998:

vai(xx,yy,vel)
{
 drive(dir=180+180*(xx>loc_x())+atan(100000*(loc_y()-yy)/(loc_x()-xx)),vel);
 }

È interessante notare che la compattezza si paga: le ultime due routine contengono infatti un errore. Nel caso in cui il punto di partenza e quello di arrivo abbiano la stessa ascissa ci si trova davanti a una divisione per 0, con il risultato di far ripartire il crobot dalla prima istruzione. L' evento è quantomai raro, ma se volete stare sul sicuro usate la procedura del manuale e vivete felici.

Avete capito alla perfezione quanto detto sopra?
Allora smettetela immediatamente di leggere e iniziate a programmare, che il torneo vi attende.
Se invece, nonostante le procedure siano ben commentate, molti punti vi sono rimasti oscuri, non preoccupatevi minimamente. In seguito ogni cosa andrà al suo posto e tutto vi diventerà chiaro.
Comunque adesso e' arrivato il momento di imparare qualcosa su...

Le Convenzioni da usare:
Del formato del file, che deve essere ASCII puro, per permettere al compilatore di eseguirlo correttamente, vi ho già parlato.
Aggiungo adesso che è opportuno salvare il file con estensione *.r, per permettere a chiunque di identificarlo all' istante come il sorgente di un crobot.
E' poi consigliabile identare e commentare abbondantemente il listato, spiegando la strategia della vostra creatura e il funzionamento delle routine principali: mettetevi nei panni di chi deve leggerlo. Come vi trovereste a dover decifrare del codice disordinato e senza la più piccola nota esplicativa? Potreste arenarvi su procedure che, con l' aiuto di un semplicissimo:

/* .....(Tutto quello che c' è all' interno di /*......*/ è solo un commento e non viene eseguito)....*/

vi sarebbero immediatamente chiare.
Infine, mentre è possibile impiegare indifferentemente lettere maiuscole e minuscole per i nomi delle variabili e delle procedure che andremo a definire (solo le prime sette lettere sono significative), le istruzioni proprie del compilatore vanno inserite in minuscolo.
Dal momento che non mi sembra ci sia altro da aggiungere, possiamo passare senza indugio ad...

Analizzare il nostro primo Crobot;
1   /* ERICA.R e' un robot in C
2       Leggi il file ERICA.TXT per maggiori informazioni
3       ERICA.R e' stato scritto da...

4      XXXX YYYY
5      Esempio di crobot
6      breve, commentato
7      e di facile lettura*/
 

8      int     direz, alfa, range, dato, grado;
9      main()
10    {
11       direz = rand(360);                                                             /* DEFINISCE UNA DIREZIONE
                                                                                                                A CASO */
12       grado = rand(360);                                                            /* DEFINISCE LA DIREZIONE
                                                                                                             DEL  CANNONE */
13       while (1)
14          {
15         drive(direz, 100);
16          if (loc_y() > 850 )                                                          /* GIUNTO AI MARGINI
                                                                                                                CAMBIA */
17          direz = 180 + rand(180);                                                /* DIREZIONE
                                                                                                                CASUALMENTE */
18           if (loc_y() < 150)
19                 direz = rand(180);
20           if (loc_x() > 850 )
21               direz = 90 + rand(180);
22           if (loc_x() < 150 )
23               direz = 270 + rand(180);
24          range = scan (grado,20);                                               /* RICERCA PRESENZA
                                                                                                                ROBOTS */
25           if (range > 0 )
26                {
27                 cannon(grado, range);                                            /* CE N'E' UNO? FUOCO!!!! */
28                 cannon(grado, range);
29                }
30             else grado = grado+20;                                              /* NON C'ERA? INCREMENTA
                                                                                                                ANGOLAZIONE */
31            }

32      }

E' un crobot molto semplice e anche molto ben commentato. Un grazie all' autore, che probabilmente non immaginava l' impiego della sua opera in questo tutorial.
Analizziamolo un pò in dettaglio (i numeri di linea non fanno parte del programma e non devono essere inseriti: sono stati aggiunti per facilitare la spiegazione):
 

              main()
                    {
                         /* Tutto quello che sta tra le due parentesi graffe e' il corpo principale del
                              programma*/
                    } Per ora tutto chiaro vero? Dopotutto fino a questo si tratta di banalissime istruzioni in C standard. Ma adesso viene il bello.  Stiamo per analizzare quei comandi che ci permettono di controllare il nostro crobot, e di rendere il suo comportamento diverso da quello di qualsiasi altro combattente in circolazione: Un paio di considerazioni:
Erica.r è un ottimo programma per imparare le basi di Crobots, ma non aspettatevi di riuscire a vincere con esso una partita contro i combattenti moderni.
Innanzituto si muove a caso nelle zone centrali dello schermo, e quindi è predisposto a ricevere colpi da tutti gli altri concorrenti.
In secondo luogo la routine di fuoco è asolutamente primitiva. Non effettuando correzioni né sull' angolo né sulla distanza di sparo è assolutamente probabile che quando il proiettile raggiunge l' obiettivo, questo non sia più lì.
A questo proposito il manuale riporta questa tabellina dei casi in cui un crobot può subire dei danni:
2% - collisione con un altro robot o contro il muro. Un effetto collaterale e' lo spegnimento del motore.
3% - esplosione di un missile entro un raggio 40 di metri.
5% - esplosione di un missile in un raggio di 20 metri.
10% -esplosione di un missile in un raggio di 5 metri .
Prestate attenzione particolarmente al primo punto. Se il vostro crobot si blocca vicino ad un lato o nel mezzo dell' arena potrebbe essere quella la causa.

Comunque direi che è ora di mettere da parte Erica.r per passare a studiare un crobot che supera i limiti che ho evidenziato finora.  E' il momento di esaminare...

...Il mio crobot preferito: Arale.r
Questo programma è per molti versi il mio ideale di crobot:
Innanzitutto ha una presentazione simpatica e divertente.
In secondo luogo è versatile: non si fossilizza su una posizione, ma è programmato per poter spaziare in tutti i quattro angoli dell' arena a seconda della locazione in cui nasce.
Infine i suoi autori sono tra i pochi in circolazione che non battono sentieri ormai consolidati ma si sforzano di esplorare strade sempre nuove.
L' unico difetto è il sorgente scarsamente commentato.

*----------------------------------------------------------------------------
     AAAA    RRRRR   AAAA   LL           EEEEE
    AA   AA  RR   RR  AA  AA  LL           EE
    AAAAA  RRRRR  AAAAA LL           EEEE
    AA   AA  RR RR    AA  AA  LL           EE
    AA   AA  RR   RR  AA  AA  LLLLLL  EEEEEE

   Nome      : Arale.r  (30-09-97)

   Creatore  : Senbee Norimaki (Dr. Slump)

   Autori    : XXXXX YYYYYY
------------------------------------------------------------------------------

 Tu costruisci robot
 belli e geniali però
 tutti senza qualche rotella!
 ...

 Il robot Arale  all'inizio della partita si dirige verso l'angolo più vicino
 e comincia a muoversi rapidamente avanti ed indietro, giocando con gli altri
 robot suoi amici  con gli attacchi  Hoyoyo (più preciso), ed  Gupi_Gupi (più
 veloce).  Dopo 100000 cicli Arale si annoia e finisce la pazienza, allora si
 sposta  al centro dell'arena  e  fa  l'aereoplano  (WOOOOOOOSH!)  muovendosi
 velocemente  da un lato  all'altro e  sparando ai suoi amichetti superstiti.

----------------------------------------------------------------------------*/

int
 posy,                                                                             /* Luogo di nascita */
 pazienza,                                                                      /* Pazienza di Arale */
 ang,d;                                                                            /* Angolo e distanza degli amici di Arale */

main()
{
/*  C I R I C I A O  */
 pazienza=100;
 posy=loc_y();
1    if (loc_x() > 500) if (posy > 500)
2      {
        /* Angolo Nord-Est */
3   drive(90,100); while (loc_y()<930) hoyoyo();
4   drive(90,0);   gupi_gupi();
5   drive(0,100);  while (loc_x()<930) hoyoyo();
6   drive(0,0);    gupi_gupi();
7   while (pazienza-=1)
8    {
9     drive(180,100); while(loc_x() > 800) hoyoyo();
10     drive(180,0);   gupi_gupi();
11    drive(0,100);   while(loc_x() < 930) hoyoyo();
12     drive(0,0);     gupi_gupi();
13    }
14   drive(270,100); while (loc_y() > 520) hoyoyo();
15   drive(270,0);   gupi_gupi();

  }
  else
  {
  /* Angolo Sud-Est */
   drive(0,100);   while (loc_x()<930) hoyoyo();
   drive(0,0);     gupi_gupi();
   drive(270,100); while (loc_y()>70) hoyoyo();
   drive(0,0);     gupi_gupi();
   while (pazienza-=1)
    {
     drive(90,100);  while(loc_y() < 200) hoyoyo();
     drive(90,0);    gupi_gupi();
     drive(270,100); while(loc_y() >  70) hoyoyo();
     drive(270,0);   gupi_gupi();
    }
   drive(90,100); while (loc_y() < 480) hoyoyo();
   drive(90,0);   gupi_gupi();
  }
  else if (posy > 500)
  {
  /* Angolo Nord-Ovest */
   drive(180,100); while (loc_x()>70) hoyoyo();
   drive(180,0);   gupi_gupi();
   drive(90,100);  while (loc_y()<930) hoyoyo();
   drive(0,0);     gupi_gupi();
   while (pazienza-=1)
    {
     drive(270,100); while(loc_y() > 800) hoyoyo();
     drive(270,0);   gupi_gupi();
     drive(90,100);  while(loc_y() < 930) hoyoyo();
     drive(90,0);    gupi_gupi();
    }
   drive(270,100); while (loc_y() > 520) hoyoyo();
   drive(270,0);   gupi_gupi();
    drive(90,0);   gupi_gupi();
 }
  else
  {
  /* Angolo Sud-Ovest */
   drive(270,100); while (loc_y()>70) hoyoyo();
   drive(270,0);   gupi_gupi();
   drive(180,100); while (loc_x()>70) hoyoyo();
   drive(0,0);     gupi_gupi();
   while (pazienza-=1)
    {
     drive(0,100);   while(loc_x() < 200) hoyoyo();
     drive(0,0);     gupi_gupi();
     drive(180,100); while(loc_x() > 70) hoyoyo();
     drive(180,0);   gupi_gupi();
    }
   drive(90,100); while (loc_y() < 480) hoyoyo();
   drive(90,0);   gupi_gupi();
     drive(90,0);   gupi_gupi();
}
/* WOOOOOOSH!! (Aereoplano)*/
1 while(1)
  {
2   drive(0,100);  while (loc_x() < 900) if (scan(ang,10))
3      cannon(ang+=7*(!(scan(ang+356,7)))+353*(!(scan(ang+4,7))),scan(ang,10));
4                                        else ang+=21;
5   drive(0,0);     gupi_gupi();
6   drive(180,100); while (loc_x() > 100) if (scan(ang,10))
7      cannon(ang+=7*(!(scan(ang+356,7)))+353*(!(scan(ang+4,7))),scan(ang,10));
8                                         else ang+=21;
9   drive(180,0);   gupi_gupi();
  }
}

/* Routine di attacco standard */
hoyoyo()
{
1 if ( (d=scan(ang,10)) && (d<770) )
   {
2   if (d=scan(ang+353,3)) cannon(ang+=353,d);
3   else if (d=scan(ang,3)) cannon(ang,d);
4   else if (d=scan(ang+7,3)) cannon(ang+=7,d);
  }
 else
  {
5   if ((d=scan(ang+21,10))&&(d<700)) {ang+=21;cannon(ang,d);}
6   else if ((d=scan(ang+42,10))&&(d<700)) ang+=42;
7        else ang+=63;
  }

}

/* Routine di attacco veloce */
gupi_gupi()
{
 while (speed() > 49) if ((d=scan(ang,10))) cannon(ang,d);
                      else ang+=21;
}
 

           /*-------  C I R I C I A O    G E N T E ! ! !  -------*/

Benissimo, se siete arrivati a leggere fino a questo punto probabilmente siete interessati a programmare un crobot. Arale è certamente un ottimo combattente, tant' è vero che è riuscito a piazzarsi quarto nel torneo del 1997.
Presenta tuttavia ancora delle limitazioni: Supereremo i primi tre problemi esaminando Goblin.
Ora invece è opportuno che abbiate qualche nozione sulle routine di sparo più precise che si trovino al momento in circolazione.
Esse risolvono una volta per tutte il problema del cannone scarico, a prezzo però di un elevato ingombro in termini di codice e di tempo macchina.
Nella loro versione originale sono state inserite quattro anni fa nel Crobot Tox.r.
Da allora hanno subito solo modifiche marginali e sembra proprio che allo stato attuale delle cose nessun programmatore che aspiri alla vittoria possa farne a meno.
Vediamo un pò di che si tratta.
Le routine di fuoco:
Tox()
    {
     if(scan(ang,5))
      {
       if(scan(ang-5,1)) ang-=5;
       if(scan(ang+5,1)) ang+=5;
       if(scan(ang-3,1)) ang-=3;
       if(scan(ang+3,1)) ang+=3;
       if(scan(ang-1,1)) ang-=1;
       if(scan(ang+1,1)) ang+=1;
       if (range=scan(ang,5))
         {
           orange=range;
           oang=ang;
           if(scan(ang-5,1)) ang-=5;
           if(scan(ang+5,1)) ang+=5;
           if(scan(ang-3,1)) ang-=3;
           if(scan(ang+3,1)) ang+=3;
           if(scan(ang-1,1)) ang-=1;
           if(scan(ang+1,1)) ang+=1;
             if (range=scan(ang,10))
               {
                 aa=(ang+(ang-oang)*((1200+range)>>9)-(sin(ang-an)>>14));
                 rr=(range*160/(160+orange-range-(cos(ang-an)>>12)));
                 while(!cannon(aa,rr));
                 if (range>700) ang+=30;
               }
             else if(scan(ang-=10,10));
                  else if(scan(ang+=20,10));
                       else while ((scan(ang+=11,10))== 0);
         }
       else if(scan(ang-=10,10));
            else if(scan(ang+=20,10));
                 else while ((scan(ang+=11,10))== 0);
      }
    else if(scan(ang-=10,10));
         else if(scan(ang+=20,10));
              else while ((scan(ang+=11,10))== 0);
}
Quella sopra, e la sua variante per gli spari da fermo, sono senz' altro delle routine eccezionali. Tuttavia mirare bene non basta per vincere: serve anche una buona strategia durante l' incontro.
Avendo la possibilità di chiedere la collaborazione di tre programmatori che hanno modificato e impiegato le Tox-like in modi decisamante efficaci ho quindi deciso di farvi leggere cosa dicono del comportamento delle loro creature.
Loro sono Maurizio Camangi, Alessandro Carlin e Daniele Nuzzo, creatori di Jedi.r, Sottoliin.r e Goblin.r
E' dunque venuto il momento di cedere........
La parola agli autori:
Maurizio Camangi
E' senza dubbio il più appassionato croboticista che io conosca.
Le sue opere hanno sempre quel qualcosa che riesce a distinguerle dalla massa. Un talento raro,  forse resosi necessario a causa di un piccolo vezzo di questo simpatico giocherellone: chiama i suoi robot TUTTI con lo stesso nome ormai da anni. Infatti fino ad oggi si sono succedute almeno 5 generazioni di Hal9000.r.
Strategia di Jedi.r:
Si sposta nell'angolo a sud-est del campo di battaglia.
Oscilla a ovest ed a nord, restando vicino all'angolo.

La funzione di fuoco in movimento è piuttosto complessa e si basa sulla correzione dell'angolo e della gittata in base allo spostamento proprio e dell'avversario, ispirata dal crobot TOX.
Dopo un numero prefissato di cicli JEDI attacca un crobot utilizzando una routine tratta dal crobots LEADER.

NOTE IMPLEMENTATIVE

Il cuore del programma risiede nella funzione di fuoco TOX-like.
Una semplice aggiunta ha permesso a JEDI di incrementare la sua efficienza contro i robot statici o che oscillano in brevi spazi: subito dopo il primo

'if(range=scan(ang,5))'

la cannon() senza alcuna correzione nell'angolo e nella gittata permette, paradossalmente, la precisione contro robot che oscillano in brevi spazi, contro il quali, la TOX-fire e` poco efficace.
 La caratteristica saliente di questo robot è il movimento a "J" nel corner a sud-ovest del campo di battaglia, che lo rende un bersaglio abbastanza difficile. Purtroppo i cambi di direzione sono molto frequenti e questo fatto incide negativamente nella precisione di tiro. Dopo 150 oscillazioni, se i danni non superano l'80%, JEDI attacca con una routine tratta dal forte crobots americano LEADER. Tale routine è ottima per scontri a due robot, per questo motivo non sarebbe una cattiva idea aggiungere una routine che rileva il numero di robot superstiti allo scadere dei 150 cicli e prendere la decisione più idonea.
 JEDI è stato considerato, e ciò mi fa molto piacere, un programma "elegante" e dalla buona strategia. La resa in torneo non è stata ottima ma nonostante ciò alcuni crobottisti hanno tratto spunto da questo robot. Una delle pecche di JEDI è il  limite di oscillare sempre nello stesso angolo: spaziare nei quattro quadranti dell' arena di gioco renderebbe JEDI un combattente certamente più valido. Un'altra possibile miglioria potrebbe risiedere nel cercare di tenere il robot più vicino possibile ai bordi dell'arena virtuale durante l'oscillazione, aumentando la sicurezza; per contro quest'ultimo accorgimento non è compatibile con la funzione di fuoco, molto precisa ma  piuttosto ingombrante sia in termini di codice che di tempo di esecuzione (cicli CPU).

Alessandro Carlin
Arrivato secondo nel 1996 e terzo l' anno scorso Alessandro quest' anno ha due prototipi che sembrano veramente formidabili. Per adesso si limita a parlarci del suo Uomo Invisibile.

Nome del crobot: ________.r - The invisible man
                 (nel torneo 1998 ha partecipato col nome "sottolin.r" per
                  motivi estetici (?))
Dopo due anni in cui avevo spedito crobots tra loro molto simili (Rudolf e Rudolf_2) con buoni risultati, la strategia è cambiata radicalmente: The invisible man si porta innanzitutto nella parte bassa dell' arena, e si guarda rapidamente a destra e a sinistra per decidere dove andare, scegliendo l' angolo libero (sperando che almeno uno lo sia); durante il match non lascerà la posizione se non per l' attacco finale.
Nell' angolo il crobot oscilla verticalmente o orizzontalmente, e la scelta viene effettuata osservando i due angoli adiacenti: se quello superiore è vuoto, il moto avviene in direzione verticale, altrimenti in orizzontale (________.r non stà mai fermo). La larghezza dell' oscillazione è di circa 200 metri.
Questo tipo di movimento è stato ispirato da Diabolik.r.
Durante il match il crobot spara usando due routine: la prima presa da Hal9000 dell' anno precedente (di Hal ce n'è uno all' anno) molto precisa ma anche lenta e macchinosa viene usata durante l' allontanamento dall' angolo e all' inizio del movimento di ritorno, la seconda è molto più semplice ma più rapida da eseguire, quindi The invisible man riesce ad avvicinarsi al muro con più sicurezza, senza sbatterci il naso (evitando così un 2% di danni a botta).
Durante l' incontro il crobot conta di tanto in tanto i nemici e se ne trova uno solo lo attacca oscillando a destra e sinistra per tutta la larghezza del campo, a metà altezza. Questo modo di attaccare è stato ispirato da Arale.r ed è solo stata migliorata la routine di sparo con una semplice correzione sul range.
Purtroppo ho scoperto in seguito che l' efficienza del robot sarebbe aumentata se anche nella fase di attacco avessi usato la routine di sparo "macchinosa" di Hal9000, con conseguente risparmio di spazio e, forse, la possibilità di far cambiare angolo al crobot durante l' incontro senza sfondare il tetto delle 1000 istruzioni macchina.
C' è da dire che se entrambi gli angoli sono occupati, ________.r preferisce oscillare orizzontalmente, e questa scelta è stata motivata dal fatto che l' altro mio crobot Vision.r si comporta in maniera simile nella parte NORD dell' arena; in questo modo i due crobot si intralciano tra loro il meno possibile.
Il fatto che i due crobot siano UGUALI a parte il posizionamento iniziale, e che uno sia arrivato terzo e l' altro ben più indietro testimonia come piccole variazioni influenzino anche pesantemente le prestazioni, anche in funzione degli avversari.
Quindi prima di spedire i vostri gioiellini guerrieri testateli bene e, attenzione, perchè Cyborg.r è in agguato e Rudolf.r sta per reincarnarsi per la quarta (!) volta.

Daniele Nuzzo
Per quanti non sapessero chi è Daniele Nuzzo dirò semplicemente che è uno degli autori di Crobot più geniali in circolazione: il suo B52.r ha rischiato di classificarsi al primo posto del 1995, piazzandosi dietro a Tox.r con uno scarto di pochi punti. Daniele si è rifatto nel 1997  vincendo con Diabolik.r , e ha bissato poi  (primo nella storia della competizione) il successo nel 1998, anno in cui ha dominato portando Goblin.r e Tornado.r in vetta alla classifica.
Vale quindi senza dubbio la pena leggere quanto ci scrive della sua invincibile creatura:

All'inizio del gioco i crobots in gara sono ben quattro! E' quindi molto improbabile uscirne vincenti buttandosi immediatamente in un attacco kamikaze; dopo tutto si può sparare contro un avversario alla volta, mentre si potrebbero incassare tre proiettili nello stesso tempo: non conviene! I crobots sono collocati in maniera casuale all' interno dell' arena, ma se dividiamo il campo di gioco (1000x1000) in quattro quadranti (500x500) ogni crobots si troverà in un quadrante diverso dagli altri; quindi inizialmente il posto più sicuro nell'arena è l'angolo più vicino, cioè quello del proprio quadrante. Goblin infatti lo raggiunge subito:

    if (loc_x()<500) sx(); else dx();
    if (loc_y()<500) dw(); else up();

A questo punto si deve scegliere una strategia di gioco per la fase iniziale (quella in cui ci sono 3 avversari); Goblin adotta la seguente:         if (UpDown())...     while ((!orng || orng>450) && (damage()<dam+4))...  if (UpDown()) Move(); Durante tutto il match Goblin controlla quanti avversari sono rimasti e se è solo uno lo attacca passando alla fase finale del gioco. Il controllo viene effettuato utilizzando la funzione Radar(), che è chiamata quando per oltre 15 volte consecutive il robot nemico individuato si trova a distanza superiore a 700, cioè fuori dal bersaglio.

    Radar()
    {
        deg=-10; t=0;
        while((deg+=20)!=710) if (scan(deg,10)) ++t;
        if (t<3) return 1;
        t=0;
        return 0;
    }

ATTACCO FINALE:

La routine di attacco finale prevede l'utilizzo delle due grandi diagonali e si utilizza un piccolo accorgimento: il crobot non va mai incontro ad un avversario che si trova in un angolo; infatti dopo aver percorso metà diagonale (il crobots si trova piu' o meno nel centro dell'arena) si controlla che l'angolo in cui si sta per andare sia libero, in caso contrario si cambia la diagonale di attacco. Questo movimento è ideato per cercare di mantenere il più possibile la massima velocità e per cercare di fare variare maggiormente l'angolo di fuoco all'avversario: è decisamente più semplice trovare la distanza giusta piuttosto che l'angolo!!!

    Stop()
    {
        if (loc_x()<500) if (loc_y()<500) q=0; else q=3;
                           else if (loc_y()<500) q=1; else q=2;
        dir=45+90*q;
        drive(dir,0);
    }

    diag()
    {
        while ((loc_x()<450) || (loc_x()>550)) { drive(dir,100); Fire(); }

        if ((scan(dir-10,10)) || (scan(dir+10,10)))
        {
            if ((scan(dir-80,10)) || (scan(dir-100,10))) { dir+=90;
drive(dir); }

else { dir-=90; drive(dir); }
        }

        while ((loc_x()<850) && (loc_x()>150) &&
                  (loc_y()<850) && (loc_y()>150)) { drive(dir,100);
Fire(); }
        Stop();
    }

ROUTINE  DI FUOCO:

Le routine di sparo appartengono alle cosiddette toxiche (da Tox.r o anche dall'effetto letale o tossico  che producono), anche se in realtà a Tox.r devono soltanto un miglioramento (di sicuro in lunghezza di codice e forse anche in efficienza) della routine di puntamento che in Lazyii.r, il vero ideatore di tali routine, si chiama Find(). Per maggiori spiegazioni si consiglia vivamente di fare riferimento ai sorgenti di questi due crobots. In breve si può dire che appartengono alla categoria delle ruotine di fuoco che fissano due punti in cui avvistano il robot avversario e cercano di calcolare un terzo punto in cui sparare. A differenza di altre routine utilizzano una procedura di puntamento che avviene in tempo costante (in Lazyii) o quasi (in Tox) e si avvalgono di una formula per il calcolo del terzo punto molto efficace. Per la cronaca, forse il difetto della routine di puntamento di Lazyii era l'eccessiva precisione, che di tanto in tanto portava alla perdita del bersaglio.

CONSIDERAZIONI:

E' sicuramente importante attaccare per primi contro l' ultimo avversario e infatti Goblin cerca di farlo, ma è altrettanto importante difendersi bene: in caso di attacco 1 contro 1, Goblin oscilla lungo una parete verticale e, essendo attaccato, non ha bisogno di portarsi a tiro dall'avversario; inoltre se la parete è quella ad est allora l'avversario di tanto in tanto si perderà il bersaglio per via del bug: una situazione accettabile!

Le Toxiche sono molto efficaci, ma soffrono tremendamente i movimenti oscillatori rapidi (introdotti da B52.r): le inversioni di movimento e le accelerazioni continue tendono a spiazzare le routine che cercano un terzo punto. D' altra parte le routine di fuoco utilizzabili nelle oscillazioni brevi devono essere necessariamente veloci, altrimenti le oscillazioni non sarebbero più tanto brevi ed inoltre si rischierebbe di andarsi a spalmare sulle pareti dell' arena; infine quando un crobots diminuisce la velocità diventa un facile bersaglio anche per le routine di fuoco più semplici che non utilizzano particolari correzioni. Un dualismo che probabilmente si riproporrà anche quest'anno o forse uscirà qualcosa di nuovo (sono 4 anni che non si fanno progressi significativi in tal senso)?

Abbiamo parlato di attacco e di routine di fuoco, ma non si deve dimenticare che la cosa più importante è probabilmente la strategia iniziale. Al lavoro dunque e in bocca al lupo...

Che altro aggiungere?
Niente, Daniele è stato chiarissimo.
Ormai avete tutto il necessario per costruirvi in casa il vostro robot. Sapete anche in quale direzione lavorare.... potreste essere voi a migliorare le inossidabili Toxiche!
Ribadisco solo che Goblin.r nel 1998 ha vinto con oltre 8 punti percentuali di vantaggio sul secondo (sempre della scuderia di Daniele) e ha lasciato il terzo a ben 14 punti di distanza.
Mettetevi al lavoro, vi aspettiamo al torneo!!!!!!!!
 


Simone Ascheri