`` 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!La dotazione iniziale:
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!
Quello che ci serve per iniziare a sviluppare le nostre temibilissime macchine da battaglia è ridotto ai minimi termini:Il linguaggio di sviluppo: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.
- Un computer (indispensabile)
- Un sistema operativo
- Un text editor che generi file in formato ASCII
- Una versione a scelta di CROBOT tra quelle disponibili: AmigaDOS, Linux, MS-DOS
- Tutti i sorgenti dei combattenti scritti finora, per attingere a piene mani al meglio della produzione mondiale.
- La pazienza di 'allenare' un Crobot.
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.
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.Le Convenzioni da usare:
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...
Del formato del file, che deve essere ASCII puro, per permettere al compilatore di eseguirlo correttamente, vi ho già parlato.Analizzare il nostro primo Crobot;
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...
1 /* ERICA.R e' un robot in C...Il mio crobot preferito: Arale.r
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()
- Le linee dalla 1 alla 7 sono dei semplici commenti, il compilatore le ignora
- Alla linea 8 sono dichiarate le variabili: è INDISPENSABILE farlo nella procedura principale (pena strani e incomprensibili malfunzionamenti del crobot), mentre per le sub-routine è solo facoltativo.
- Alla linea 9 inizia il programma vero e proprio: mentre i nomi di tutte le procedure possono essere scelti arbitrariamente, il corpo principale DEVE chiamarsi
{
/* 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:
- Le linee 11 e 12 scelgono casualmente, tramite la funzione rand(), alcuni parametri (la direzione di marcia e il puntamento del cannone)
- La linea 13 inizia un ciclo senza fine: tutto ciò che sta tra le parentesi graffe delle linee 14 e 31 viene ripetuto fino alla distruzione del robot o al termine della partita. Questo accade perchè l' istruzione while(1) è sempre vera: per interrompere il loop bisognerebbe infatti che l' espressione tra parentesi fosse nulla, ma essendo il numero 1 una costante, non puo' mai assumere valore 0.
Un paio di considerazioni:
- Troviamo infatti per prima cosa le funzioni loc_x() e loc_y(). L' arena è un quadrato di lato 1000 unità con l' origine degli assi (il punto 0,0) fissata nell' angolo in basso a sinistra (esattamente come nel piano cartesiano standard). Le 2 funzioni restituiscono un valore compreso tra 0 e 999 che corrispondenti al punto in cui si trova il nostro robot all' interno del campo di battaglia.
- Alla linea 24 incontriamo la funzione scan(ang,ampiezza): rappresenta gli occhi del nostro robot. Essa è in grado di individuare i robot nemici guardando nella direzione che le indichiamo con ang e con l' apertura che scegliamo. Più l' ampiezza è elevata (può essere di 10 gradi al max alla destra e alla sinistra della direzione scelta, per un totale di 20 gradi) più è probabile trovare il nemico, ma anche meno facile centrarlo con un proiettile. La funzione restituisce un numero intero compreso tra 1 e 999 che rappresenta la distanza a cui è stato individuato il nemico, oppure 0 se non è stato trovato nulla.
- La linea 25 controlla che ci sia un nemico: in effetti è persino troppo lunga. In caso fossimo a corto di spazio per il codice l' espressione if (range) sarebbe una adeguata sostituta.
- Le linea 27 spara al nemico nella direzione scelta e con la gittata che abbiamo ricavato dalla scan(). E' importante ricordarsi che l' arena ha lati di 1000 metri: il nemico può quindi essere distante da noi fino a 1440 unità, mentre la massima distanza a cui il proiettile è efficace sono 700 metri. Pertanto, se la scan() trova un nemico lontano da noi 900 unità è inutile fare fuoco.
- La linea 28 è inutile: il cannone deve avere il tempo di ricaricarsi dopo aver esploso un colpo. A questo proposito il manuale di Crobots per AMIGA riporta una tabella dei tempi richiesti dalle varie funzioni che non è presente nella versione Dos/Linux. Eccola qui.
Leggetela attentamente perchè vi sarà molto utile in futuro, soprattutto quando esamineremo le routine di fuoco derivate da Tox.r
Action Cpu Cycles
---------------------------------------------------
Recharge PPC 150
Acc/Decellerate 10% 10
Pulse traveling 50 meters 10
Robot move 7 meters
at 100% speed 1
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: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.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 .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...
Questo programma è per molti versi il mio ideale di crobot:Le routine di fuoco:
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 EEEEEENome : 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();}
- La main() è composta da 5 blocchi, di cui i pimi 4 sono strutturalmente identici: descrivono infatti il comportamento del robot in relazione al quadrante in cui e' posizionato. Le linee tra la 1 e la 15 si riferiscono all' angolo Nord-Est.
- Le linee dalla 3 alla 6 posizionano Arale.r nel punto da cui inizierà il suo movimento oscillatorio
- Alla 7 inizia un loop, che dura circa 100000 cicli virtuali di CPU. Si arresta quando la variabile 'pazienza' raggiunge il valore 0 (false).
- Le linee tra la 9 e la 12 forniscono il moto oscillante al Crobot e richiamano le due funzioni di sparo di cui è dotato. Questa strategia consente di essere sufficientemente mobili da non diventare facili bersagli dei nemici che girano nello schermo, ma anche di rimanere lontani da quelli che si annidano in un angolo e aspettano gli avversari. Le continue inversioni di marcia poi, rendono più difficile il calcolo del terzo punto di fuoco tramite interpolazione.
- Terminato il ciclo di attesa le linee 14 e 15 portano Arale alle coordinate (900,500), per iniziare la fase finale di attacco.
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();}
- Vedi la prima parte: l' angolo è quello Sud-Est
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();}
- Vedi la prima parte: l' angolo è quello Nord-Ovest
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();}
- Vedi la prima parte: l' angolo è quello Sud-Ovest
/* 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();
}}
- Quest' ultimo blocco della main() è quello che si occupa dell' attacco finale. Indipendentemente dal numero dei sopravvissuti, o dal livello dei danni personali subiti, Arale va allo sbaraglio. Non sarebbe male invece fare almeno un conto del numero degli avversari rimasti in campo. Vedremo poi con Goblin come questo sia possibile, nonostante non esista nel compilatore una funzione apposita.
- Il ciclo è infinito: quando inizia la fase finale Arale non può più pentirsi e tornare indietro.
- La strategia consiste nel muoversi parallelamente all' asse delle ascisse avanti e indietro alla massima velocità (linee 2 e 6)
- Le routine di fuoco utilizzate sono di nuovo due: La gupi_gupi(), per lo sparo veloce, che viene impiegata solo mentre il crobot rallenta, e un' altra, senza nome, che trovate alle righe 3 e 7.
- Quest' ultima cerca di combinare la rapidità e la precisione del tiro: il range viene ricavato pochi istanti prima che il proiettile parta, mentre l' angolo di fuoco viene corretto 'al volo' aggiungendo e sottraendo il risultato di due scan() successive effettuate intorno alla direzione scelta.
/* 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;
}}
- La hoyohoyo() è la routine di fuoco più precisa di cui dispone Arale. E' anche molto compatta, e questo a volte può rivelarsi un inconveniente: è infatti possibile che quando viene trovato un nemico il cannone non sia ancora pronto per sparare, perdendo così l' opportunità di danneggiare l' avversario.
- Il funzionamento e' semplice: se la linea 1 trova un robot abbastanza vicino le linee tra la 2 e la 4 cercano di puntarlo con maggiore precisione riducendo l' ampiezza della scansione, e poi fanno fuoco.
- Se invece un nemico non c' è, la linea 5 prova a cercarlo nei 20 gradi successivi.
- Se neanche questa volta si è avuto successo lo scan() si sposta in avanti di altri 20 gradi.
/* 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.
- La gupi_gupi() è la routine di attacco veloce: non effettua alcuna correzione né sull' angolo né sulla distanza. Per questo viene usata solo in condizioni particolari come il rallentamento prima di cambiare direzione. Dal momento che il cambio di direzione è consentito solo a velocità inferiori alle 50 unità, il ciclo di while() viene eseguito fino a quando la funzione speed() non ritorna un valore minore di 49. Questo dà il tempo di richiamare la funzione di fuoco almeno una mezza dozzina di volte.
Presenta tuttavia ancora delle limitazioni:Supereremo i primi tre problemi esaminando Goblin.
- Innanzitutto non controlla mai se il suo angolo è occupato.
- Non tiene conto dei danni che subisce.
- Non si cura di quanti nemici sono in circolazione prima di attaccare.
- E infine la routine di fuoco ha ancora il problema del caricamento del proiettile.
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.
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))
- Il blocco di sei scan() qui sopra, e il suo gemello posto immediatamente sotto, hanno una duplice funzione: ridurre l' ampiezza della ricerca per ottenere dei risultati sempre più accurati, e fornire una temporizzazione efficace. Infatti il compilatore non ha alcuna istruzione per permetterci di conoscere quanto tempo è trascorso da una certa operazione in poi. Bisogna quindi simulare tale funzione.
{
orange=range;
oang=ang;if(scan(ang-5,1)) ang-=5;
- Se viene trovato nuovamente un nemico, le informazioni relative a distanza ed angolo vengono memorizzate, dopo di che si procede a raffinare nuovamente la ricerca come nel punto precedente.
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));
- Adesso, conoscendo la posizione del nemico in un dato momento e il suo spostamento dopo un certo periodo, è possibile interpolare angolo e distanza per sparare poi nella posizione dove è più probabile si sia traferito. E' il compito che svolgono le due linee qui sopra.
if (range>700) ang+=30;
- Dato che è comunque possibile che il cannone non sia ancora carico quando siamo pronti a sparare, la while ripete l' operazione fino a che il proiettile non parte.
}
else if(scan(ang-=10,10));
else if(scan(ang+=20,10));
else while ((scan(ang+=11,10))== 0);}
- Questo è il primo dei tre blocchi che, in caso di assenza di nemici nel punto iniziale, ampliano la ricerca nei 20 gradi circostanti.
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.La parola agli autori:
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........
E' senza dubbio il più appassionato croboticista che io conosca.Maurizio Camangi
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).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.Alessandro CarlinNome 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:
- sx(), dx(), dw(), up() servono a spostarsi rispettivamente verso sinistra, destra, in basso ed in alto finchè non si raggiunge il perimetro del campo.
if (UpDown())...
- controlla l'angolo adiacente in direzione della parete verticale e se è libero lo raggiunge;
while ((!orng || orng>450) && (damage()<dam+4))...
- rimane fermo nell'angolo finchè non viene colpito con una certa precisione (almeno 5% di danni) oppure finchè un altro crobots si avvicina entrando nel quadrante;
if (UpDown()) Move();
- cambia angolo andando in uno dei due adiacenti: in quello in verticale se non c' è nessuno, altrimenti in quello in orizzontale;
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.
- UpDown() restituisce true(1) se non è stato possibile spostarsi nell'angolo adiacente in alto o in basso, perchè era occupato da un avversario. Move() esegue lo spostamento nell'angolo adiacente a sinistra o a destra
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 scansione viene effettuata a partire da 10 e non da zero per cercare di non cadere nel bug sull'asse zero: scan(0,10) non vede i crobots che si trovano tra -10 e -1!!! Per sicurezza inoltre viene esplorata due volte tutta l'arena. Inoltre si aspetta 16 volte prima di utilizzare Radar() per lo stesso problema del bug (senza sarebbero sufficienti 4 volte) e per evitare i casi in cui 2 o 3 crobots avversari sono allineati nella stessa direzione; se dopo 16 volte sono ancora allineati, allora probabilmente saranno molto danneggiati a causa della vicinanza ed è quindi auspicabile comunque un attacco.
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:
- q naturalmente rappresenta il quadrante; c'è anche un altro piccolo accorgimento che permette di combattere contro 2 avversari: infatti quando l'angolo è occupato si sceglie in quale direzione cambiare diagonale controllando che sia libera e che quindi non ci sia un secondo nemico
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 | |