KULT Underground

una della più "antiche" e-zine italiane – attiva dal 1994

Otto regine

3 min read

Otto regine

Per chi fosse interessato alla programmazione in C ecco un esempio di utilizzo di tecniche quali la ricorsività e la memorizzazione di matrici a due indici mediante vettori.
Il programma che viene presentato di seguito è stato realizzato dal professor
Zironi dell’Università di Modena per il corso di Teoria e Applicazioni alle
Macchine Calcolatrici. In questa sede si vuole fornire un’analisi del listato per permettore al lettore di apprezzare metodi che possono risultare molto utili in determinate situazioni.

Il programma.

Supponendo di avere una scacchiera di 8×8 caselle, vi si vogliono disporre 8 regine in modo tale che, alla prima mossa, non si trovino in presa reciproca.
Si ricorda che nel gioco degli scacchi la regina si può muovere in orizzontale, verticale e lungo linee parallele alle diagonali principale e secondaria.

L’algoritmo.

1. Si posiziona la prima regina (preferibilmente nella casella in alto a sinistra);
2. si posiziona la seconda regina in modo tale che non sia in presa reciproca con la prima;
3. si procede in questo modo finchè:

a. si arriva a posizionare l’ottava regina, oppure

b. si esauriscono le caselle in cui si possono posizionare le

regine senza avere sistemato l’ultima.

Nel caso a. si stampa la soluzione, nel caso b. si riposiziona l’ultima regina e si riprende dal punto 3.

L’espediente.

Ogni volta che si sistema una regina sulla scacchiera, occorre sapere in quali caselle sarebbe presa dalle regine precedentemente posizionate. Per tenere memoria di queste caselle vengono utilizzati tre vettori: lr (righe libere), ldp (diagonali parallele alla principale libere), lds (diagonali parallele alla secondaria libere). In essi il valore 1 indica che nella casella
è possibile mettere una regina, il valore 0 indica il caso contrario. Si noti che non vengono considerate le colonne in quanto il loro indice viene
utilizzato come indice di variazione dell’algoritmo.
In questo modo, senza utilizzare una matrice a due indici per rappresentare la scacchiera, siamo in grado memorizzare le posizioni di tutte le regine.
Supponendo di considerare dati di tipo carattere (1 byte), i vantaggi ottenuti sono molteplici:
1. innanzi tutto si può notare che volendo mantenere in memoria una matrice di 8×8 elementi occorrono 64 byte, mentre per memorizzare i vettori lr, ldp ed lds bastano 38 byte, in quanto 8 sono le righe e
15 le diagonali sia parallele alla principale che alla secondaria. In questo modo si ha un discreto risparmio di memoria già per una scacchiera di dimensioni ridotte come quella utilizzata in questo caso.
2. Un altro vantaggio deriva dal minor numero di celle contigue necessarie per memorizzare l’intera struttura. I vettori, infatti, possono essere allocati in tre aree di memoria differernti; la matrice, invece, necessita di 64 celle contigue per essere memorizzata.
3. Abbiamo inoltre un più rapido accesso agli elementi memorizzati grazie al minor numero di indici utilizzati.

Numerazione delle diagonali.

Sistema utilizzato per numerare le diagonali parallele alla principale.

Sistema utilizzato per numerare le diagonali parallele alla secondaria.

Listato programma.

(Cliccare qui per scaricare il file REGINE.C)

include
void prova(int ic); void stampa_soluzione(); int ns, r[8], lr[8],

ldp[15], lds[15],

v[10][8];

void main()
{
int i;
for(i=0;i<8;i++)
lr[i]=1;
for(i=0;i<15;i++)
ldp[i]=lds[i]=1;
prova(0);
}
void prova (int ic)
{
int ir;
for(ir=0;ir<8;ir++)
if(lr[ir])
if(ldp[ir-ic+7])

if(lds[ir+ic])

{

lr[ir]=ldp[ir-ic+7]=lds[ir+ic]=0;

r[ic]=ir;

if(ic<7)

prova(ic+1);

else

stampasoluzione();

lr[ir]=ldp[ir-ic+7]=lds[ir+ic]=1;

}

}
void stampasoluzione(void)
{
int i,j,k,l,diversa,uguali,

w[8],ra[8];

for(i=0;i<8;i++)
w[i]=r[i];
diversa=1;
i=0;
while((i{
j=0;
while((j<4)&&diversa)

{

for(k=0;k<8;k++)

ra[k]=w[k];

j++;

for(k=0;k<8;k++)

w[ra[k]]=7-k;

l=0;

while((l<2)&&diversa)

{

l++;

for(k=0;k<8;k++)

ra[k]=w[k];

for(k=0;k<8;k++)

w[ra[k]]=k;

uguali=1;

k=0;

while((k<8)&&uguali)

{

uguali=(w[k]==v[i][k]);

k++;

}

diversa=1-uguali;

}

}

i++;
}
if(diversa)
{
for(i=0;i<8;i++)

v[ns][i]=r[i];

clrscr();
printf(“nn Soluzione n.%2d: “,ns);
for(i=0;i<8;i++)

printf(“%d “,r[i]);

printf(“nn”);
for(j=0;j<8;j++)

{

printf(” +”);

for(i=0;i<8;i++)

printf(“—–+”);

printf(“n”);

for(i=0;i<8;i++)

{

if(r[i]==j)

printf(” .”);

else

printf(” .”);

}

printf(“n”);

}

printf(” +”);
for(i=0;i<8;i++)

printf(“—–+”);

printf(“n”);
ns++;
getch();
}
}

La funzione “prova()”.

E’ un esempio di ricorsività diretta che qui viene utilizzata per la ricerca della posizione delle regine sulla scacchiera. In questo particolare caso risulta appropriato l’uso di una funzione di questo tipo in quanto l’algoritmo
è tipicamente ricorsivo, pertanto la sua realizzazione mediante un ciclo variato sull’indice di colonna non permetterebbe, alla fine del ciclo, di ritornare alla penultima regina se non mediante indici ausiliari. La struttura ricorsiva, invece, realizza automaticamente il processo di ricerca all’indietro.

La funzione “stampa_soluzione()”

Questa funzione realizza la stampa sullo schermo della scacchiera in cui il simbolo indica la posizione di una regina: E’ da notare che non vengono stampate soluzioni ottenibili per rotazione o simmetria da soluzioni precedentemente trovate.

Elena

Commenta