You are on page 1of 71

POLITECNICO DI TORINO

III Facolt di Ingegneria dell'Informazione


Corso di Laurea in Ingegneria Informatica

Tesina di Intelligenza Articiale

Primo step risolutivo del cubo di Rubik


Implementazione e applicazione dell'algoritmo A*

Docente:
Prof. Elio Piccolo

Studenti:
Gian Marco Giorgio

Pazzola Sogos

179803

177905

Anno accademico 2011-2012

Indice
1 Il cubo di Rubik 2 Il metodo Fridrich 3 Digitalizzazione del contesto
3.1 3.2 3.3 Cubo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mosse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1 6 8
8 9 10

Scramble . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4 Implementazione dell'algoritmo A*
4.1 4.2 4.3 Scelta della funzione di valutazione . . . . . . . . . . . . . . . . . . . . . . . . . . . Riconoscimento delle simmetrie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementazione dell'algoritmo . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12
13 17 18

5 Applicazione dell'algoritmo 6 Codice sorgente Bibliograa

22 25 68

ii

Capitolo 1

Il cubo di Rubik
Il cubo di Rubik un celebre rompicapo inventato dal professore di architettura e scultore ungherese Ern Rubik nel 1974.

Il Cubo presenta nove quadrati su ognuna delle sue sei facce, per un totale di 54 quadrati. I quadrati dieriscono tra loro per il colore, con un totale di sei colori dierenti. Quando il Cubo di Rubik risolto, ogni faccia ha tutti i nove quadrati dello stesso colore.

Figura 1.1. Rappresentazione di un cubo di Rubik.


Il cubo di Rubik composto da tre assi perpendicolari tra loro su cui sono montati i sei centri del cubo, tali centri sono dunque ssi e possono ruotare solo su se stessi (vedi Figura 1.2). Perci si hanno sempre le seguenti propriet:

il centro opposto al rosso l'arancione;

il centro opposto al blu il verde;

il centro opposto al bianco il giallo.

1  Il cubo di Rubik

Figura 1.2. Rappresentazione dei centri.


Ogni coppia di centri racchiude uno spigolo per un totale di dodici. Essi sono caratterizzati da due colori. In Figura 1.3 mostrato lo spigolo rosso-bianco.

Figura 1.3. Rappresentazione dello spigolo rosso-bianco.


Ogni terna di spigoli circonda un angolo, complessivamente un cubo di Rubik ne ospita otto. In Figura 1.4 rappresentato l'angolo rosso-bianco-verde, racchiuso tra la terna di spigoli rosso-verde, bianco-rosso e verde-bianco.

Figura 1.4. Rappresentazione dell'angolo rosso-bianco-verde..


Questa particolare costruzione permette di poter eettuare esclusivamente degli specici movimenti attraverso la rotazione di

90 di un centro.

Tale rotazione implica lo spostamento contemporaneo

di quattro spigoli e quattro angoli e coinvolge allo stesso tempo cinque facce. L'unica faccia che non subir cambiamenti quella che giace attorno al centro opposto a quello che viene ruotato. In Figura 1.5 mostrata una rotazione di -90 della faccia gialla. Per poter rappresentare tutte le possibili congurazioni, ottenibili eettuando un qualsiasi movimento a partire da una determinata congurazione, occorre introdurre un'appropriata terminologia

1  Il cubo di Rubik

Figura 1.5. Rotazione del centro giallo.


in cui ciascuna faccia identicata univocamente rispetto al punto di vista dell'osservatore (vedi Figura 1.6). La notazione da noi adottata :

F: front la faccia frontale rispetto all'osservatore;

R: right la faccia alla destra di front ;

B: back la faccia opposta a front ;

L: left la faccia alla sinistra di front ;

D: down la faccia inferiore del cubo;

U: up la faccia opposta a down.

Figura 1.6. Notazione per le diverse facce rispetto al punto di vista dell'osservatore.
Di conseguenza una rotazione di 90 della faccia frontale in senso orario sar identicata con il simbolo f, mentre una rotazione di -90 sar identicata con f '

1 .

La Tabella 1.1 illustra le diverse congurazioni ottenute a fronte della rotazione di una qualsiasi delle facce in senso orario e antiorario. Le lettere maiuscole faranno sempre riferimento alle facce del cubo, mentre le lettere minuscole faranno riferimento al movimento della faccia corrispondente.

1 nel

codice C tutte le rappresentazioni di rotazioni di -90 che qui sono indicate con l'apice saranno sostituite

dal numero 1. Ad esempio f ' diventer

f1

1  Il cubo di Rubik

Mossa 3D

orario 2D 3D

antiorario 2D

Tabella 1.1. Rappresentazione delle mosse ottenute ruotando una faccia del cubo a partire dal cubo risolto.

Oltre alle mosse elencate n'ora possibile eettuare altri tre tipi di movimenti ossia quelli dati dalla rotazione attorno ai tre assi del cubetto virtuale presente al centro del cubo. Questa categoria di mosse interessa solo gli spigoli compresi tra i quattro centri che vengono traslati, mentre la posizione e l'orientamento degli angoli rester immutata.

1  Il cubo di Rubik

Questi movimenti prendono il nome di:

m: meridian rotazione attorno all'asse RIGHT-LEFT ;

e: equator rotazione attorno all'asse UP-DOWN ;

s: standing rotazione attorno all'asse BACK-FRONT ;

come ragurato in Tabella 1.2. Mossa 3D orario 2D 3D antiorario 2D

Tabella 1.2. Rappresentazione delle mosse ottenute dalla rotazione attorno ai tre assi del cubetto virtuale presente al centro del cubo.

Capitolo 2

Il metodo Fridrich
Il Metodo Fridrich il metodo di risoluzione del cubo di Rubik creato da Jessica Fridrich agli inizi degli anni ottanta ed uno dei metodi pi usati dai partecipanti alle competizioni di speedcubing. Non il metodo pi complesso in assoluto, ma di fatto non un metodo adatto ai principianti. Consiste nella memorizzazione ed esecuzione di quattro fasi e ad eccezione della prima, per ricomporre il cubo bisogna memorizzare un totale di 119 algoritmi. dall'acronimo CFOP: Cross, F2L, OLL, PLL. Le quattro fasi sono sintetizzate

Cross: consiste nel posizionare gli spigoli di una faccia del cubo in modo da formare una croce tale che entrambi i colori di ogni spigolo siano allineati con i relativi centri. Per questa fase non ci sono algoritmi ma si esegue basandosi sull'intuito e sull'esperienza: il numero di mosse per fare la croce dovrebbe essere limitato o superare di poco gli otto movimenti del cubo (un esempio mostrato in Figura 2.1).

Figura 2.1. Esempio di una croce bianca.

F2L: lo scopo di questa fase quello di completare i primi due strati del cubo. Questo fatto inserendo le quattro coppie angolo spigolo tra i pezzi della croce sistemati nel primo passo, come mostrato in Figura 2.2. I casi che si possono presentare in questa situazione sono 41 (22 + 19 casi simmetrici) i quali possono essere risolti mediante l'utilizzo di algoritmi prestabiliti.

2  Il metodo Fridrich

Figura 2.2. Esempio F2L.

OLL: consiste nell'orientare l'ultimo strato del cubo in modo da completare la faccia opposta a quella della croce (nell'esempio si dovranno orientare i pezzi dell'ultimo strato in modo da ottenere la faccia gialla come in Figura 2.3). I casi che si possono presentare in questa situazione sono 57.

Figura 2.3. Esempio OLL.

PLL: in questa fase occorre permutare i pezzi dell'ultimo strato ancora fuori posto in modo da completare il cubo (vedi Figura 2.4). I casi che si possono presentare in questa situazione sono 21.

Figura 2.4. Cubo risolto.

Capitolo 3

Digitalizzazione del contesto


Questo capitolo ha lo scopo di illustrare come si passati dalla rappresentazione descritta in precedenza al codice di programmazione C.

3.1

Cubo

Per la rappresentazione del cubo si scelta una struttura dati composta da sei matrici di dimensione 3x3. Esse rappresentano rispettivamente le facce del cubo F, R, B, L, D e U; questo suggerisce che il punto di vista della macchina sso (il cubo non sar mai virtualmente ruotato). I colori sono rappresentati da un char che contiene la lettera iniziale del colore:

r: red ;

g: green ;

o: orange ;

b: blue ;

y: yellow ;

w: white.

typedef struct cubo{ char F[3][3]; char R[3][3]; char B[3][3]; char L[3][3]; char D[3][3];

3  Digitalizzazione del contesto

char U[3][3]; } cubo;


Il cubo sar stampato seguendo la notazione gi utilizzata in precedenza, come in Figura 1.6. Di seguito mostrato un esempio di stampa in cui la faccia frontale rossa, quella di destra verde, quella inferiore bianca e le altre di conseguenza.

Figura 3.1. Stampa della congurazione iniziale del cubo.

3.2

Mosse

Per rappresentare tutte le possibili mosse utilizziamo 18 funzioni, le quali spostano gli elementi delle matrici coinvolte nel movimento in modo coerente con la mossa da eseguire. Le funzioni si aspettano in ingresso un cubo generico e restituiscono in output un cubo con la congurazione derivata dalla mossa eettuata. Di seguito sono riportati i prototipi di tali funzioni:

cubo r(cubo* c); cubo l(cubo* c); cubo u(cubo* c); cubo d(cubo* c); cubo f(cubo* c); cubo b(cubo* c); cubo m(cubo* c); cubo e(cubo* c); cubo s(cubo* c); cubo r1(cubo* c); cubo l1(cubo* c); cubo u1(cubo* c);

3  Digitalizzazione del contesto

cubo d1(cubo* c); cubo f1(cubo* c); cubo b1(cubo* c); cubo m1(cubo* c); cubo e1(cubo* c); cubo s1(cubo* c);
Segue un esempio dell'applicazione della funzione right :

stampa(&start); start = r(&start); stampa(&start);

Figura 3.2. Stampa della congurazione iniziale del cubo e stampa della congurazione a fronte della mossa r.
Come possibile vedere dall'output in Figura 3.2, la stampa coerente con la congurazione illustrata in Tabella 1.1.

3.3

Scramble

Per scramble si intende una successione di mosse atte a mischiare il cubo. Nel nostro programma abbiamo predisposto una funzione che esegue 35 mosse casuali che eettuano lo scrmable. Segue un esempio della chiamata a tale funzione:

stampa(&start);

10

3  Digitalizzazione del contesto

start = scramble(&start); stampa(&start);

Figura 3.3. Stampa della congurazione iniziale del cubo e stampa della congurazione a seguito dello scramble.

11

Capitolo 4

Implementazione dell'algoritmo A*
Normalmente impossibile avere in memoria una struttura dati che descriva tutto lo spazio degli stati. Basti pensare che, per il cubo di Rubik, il numero totale delle congurazioni possibili dato dalle combinazioni di tutti gli angoli con tutti gli spigoli considerando anche le eventuali orientazioni dei singoli pezzi, ossia

8! 38 12! 212 5,19 1020 .

In realt non tutte queste congurazioni sono

realizzabili in quanto occorre tenere conto dei vincoli di costruzione del cubo, perci il numero esatto delle combinazioni

43.252.003.274.489.856.000 4,33 1019 .

Nonostante sia un sotto problema della risoluzione completa del cubo, il numero di combinazioni che devono essere analizzate per completare il primo step del metodo Fridrich decisamente elevato. Infatti, considerando che in media il numero di mosse necessarie pari a otto e che le mosse possibili che possono essere eettuate su una qualsiasi congurazione sono 18, si ha che il numero totale di nodi che devono essere tenuti in memoria per rappresentare l'albero completo, senza l'eliminazione di eventuali simmetrie, pari a

180 + 181 + 182 + 183 + 184 + 185 + 186 + 187 + 188 = 11.668.193.551

e considerando che ogni nodo rappresentato da una struttura che ha una dimensione di 68 Byte, lo spazio di memoria richiesto sarebbe pari a circa 793 GByte. Per questo motivo inopportuno utilizzare un algoritmo di ricerca in ampiezza per trovare la soluzione con il percorso minimo.

Nasce quindi spontanea la necessit di utilizzare un metodo di ricerca che costruisca un grafo che rappresenta una porzione dello spazio degli stati. A* un algoritmo di ricerca su gra che individua un percorso da un dato nodo iniziale verso un dato nodo goal. Utilizza una stima euristica che classica ogni nodo attraverso una stima della strada migliore che passa attraverso tale nodo. Il nodo che sar visitato scelto in base a tale stima euristica. La funzione di valutazione utilizzata dall'algoritmo A* fornisce una stima del costo di un cammino a costo minimo dal nodo di partenza al nodo nale vincolato a passare per un certo nodo
n dove f(n) il valore della funzione in n. La funzione di valutazione sar utilizzata per ordinare i

nodi da espandere; essa pari a

(n) = g (n) + h (n) f

dove:

12

4  Implementazione dell'algoritmo A*

g(n) il costo del cammino dal nodo iniziale al nodo n ; la stima del costo di un cammino da n a uno stato nale. Per poter calcolare

(n) h

(n) h

occorre utilizzare tutta la conoscenza euristica disponibile nel dominio del problema.

Il nostro obiettivo quello di applicare l'algoritmo A* alla prima parte del metodo Fridrich (cross), ossia l'unica che pu essere eseguita solo intuitivamente. Per fare questo abbiamo bisogno di un'euristica che ci permetta di scegliere di espandere solo i nodi pi promettenti.

4.1

Scelta della funzione di valutazione

Come gi detto in precedenza la funzione di valutazione composta da altre due funzioni: g(n) e

(n). h
La funzione g(n) tiene traccia del numero di mosse eettuate per arrivare al nodo n. Per quanto riguarda la scelta della funzione euristica

(n) h

una semplice valutazione del tipo: quanti pezzi

sono al loro posto non era suciente per far convergere l'algoritmo ad una soluzione esplorando un numero di stati ridotto; con un'euristica incompleta si correva il rischio di ricadere in una soluzione troppo onerosa in termini di memoria. Occorreva, dunque, valutare anche quanto uno spigolo fosse vicino, in termini di mosse, ad arrivare alla corretta posizione. In questo modo stato possibile discriminare situazioni in cui tutti gli spigoli erano fuori posto. Il nostro scopo quello di realizzare, a partire da una congurazione qualsiasi del cubo, la croce sulla faccia bianca. Se si prende in considerazione un singolo spigolo, in cui uno dei due

colori bianco, esso si pu trovare in 12 posizioni orientato in due modi diversi per un totale di 24 possibili congurazioni. Il numero di mosse necessarie ad inserire lo spigolo analizzato, nella posizione corretta della croce, varia da un minimo di zero (lo spigolo gi nella posizione corretta) ad un massimo di quattro mosse. Il totale di queste mosse calcolato per ogni spigolo che appartiene

(n). alla croce fornisce l'h


Per poter spiegare quante mosse sono necessarie per posizionare uno spigolo nella posizione corretta ci serviamo del seguente esempio:

per prima cosa occorre allineare uno dei due colori dello spigolo al centro corrispondente (in Figura 4.1 viene allineato lo spigolo rosso-bianco con il centro rosso);

successivamente si deve allineare il colore rimanente al rispettivo centro (in Figura 4.2 viene allineato lo spigolo rosso-bianco con il centro bianco);

Il numero di mosse necessarie per poter allineare un colore dello spigolo con il suo centro dipende dalla faccia in cui si trova il colore stesso ad esempio la faccia rossa dista 2 mosse dalla faccia

13

4  Implementazione dell'algoritmo A*

Figura 4.1. Allineamento dello spigolo rosso-bianco con il centro rosso.

Figura 4.2. Allineamento dello spigolo rosso-bianco con il centro bianco.


arancione, il che implica che, in questo caso, occorrono due mosse per poter allineare il rosso di uno spigolo al suo centro. Di seguito sono rappresentate tutte le possibili congurazioni di un singolo spigolo (rossobianco), il numero di mosse necessarie a posizionarlo e il calcolo eettuato. rappresentazioni stata ricavata l'euristica da noi utilizzata. A partire da tali

0 mosse: esiste solo un caso in cui sono necessarie zero mosse per posizionare il pezzo (vedi Figura 4.3); la parte bianca dello spigolo giace sulla faccia bianca (distanza=0) mentre la parte rossa giace sulla faccia rossa (d=0) . posizionare lo spigolo;

dtot = 0 + 0 = 0

numero di mosse totali per

Figura 4.3. Esempio dello spigolo rosso-bianco correttamente allineato.

1 mossa: nel caso lo spigolo si trovi in una delle congurazioni mostrate in Figura 4.4 sar necessaria una singola mossa per poter posizionare correttamente lo spigolo. Quando la parte rossa dello spigolo giace sulla faccia rossa (d=0) la parte bianca dello spigolo giace su una

14

4  Implementazione dell'algoritmo A*

faccia che dista 1 dalla faccia bianca (verde o blu). Analogamente quando la parte bianca dello spigolo giace sulla faccia bianca (d=0), la parte rossa giace su una faccia che dista 1 dalla faccia rossa (verde o blu). In entrambi i casi il risultato sar .

dtot = 1 + 0 = 1;

Figura 4.4. Esempio dello spigolo rosso-bianco che dista una mossa dalla posizione corretta.

2 mosse: le congurazioni che, al minimo, si risolvono in due mosse si dividono in diverse categorie:

1. In questo caso uno dei due colori dello spigolo giace sulla faccia corretta (d=0), mentre l'altro giace sulla faccia opposta rispetto al suo colore, ad esempio il bianco giace sulla faccia gialla, quindi occorrerebbero 2 mosse per posizionarlo (vedi Figura 4.5). Il risultato sar pari a

dtot = 0 + 2 = 2

mosse;

Figura 4.5. Esempio dello spigolo rosso-bianco che dista due mosse dalla posizione corretta.

2. In questo caso ciascun colore dello spigolo giace su una faccia da cui dista 1.

Ad

esempio nel primo caso in Figura 4.6 il bianco giace sull'arancione (d=1) e il rosso giace sul verde (d=1);

dtot = 1 + 1 = 2

mosse. Inoltre, per discriminare alcuni casi particolari

descritti in seguito si noti che lo spigolo non giace mai sulla linea che passa per i colori bianco-rosso-giallo-arancione (evidenziata nella Figura 4.6);

3 mosse: le congurazioni che, al minimo, si risolvono in tre mosse si dividono in due diverse categorie:

1. Questo un caso particolare, in cui se si esegue il calcolo come fatto n'ora si ottiene il valore 2, in quanto le due facce dello spigolo giacciono entrambe su facce da cui distano una singola mossa; ad esempio nel primo caso della Figura 4.7 si pu notare che il bianco dista una faccia dall'arancione cos come il rosso dista una sola faccia dal giallo.

15

4  Implementazione dell'algoritmo A*

Figura 4.6. Esempio dello spigolo rosso-bianco che dista due mosse dalla posizione corretta.
In realt occorre tenere conto del fatto che lo spigolo giace sulla linea bianco-rosso-gialloarancione, questo suggerisce la necessit di eseguire un ulteriore mossa per posizionare in modo corretto lo spigolo. Il calcolo nale sar dunque

dtot = 1 + 1 + 1 = 3

mosse;

Figura 4.7. Esempio dello spigolo rosso-bianco che dista tre mosse dalla posizione corretta.
2. In queste congurazioni un colore dello spigolo giace sulla faccia opposta al colore stesso, mentre l'altro colore giace su una faccia adiacente. Ad esempio, come mostrato in Figura 4.8, la parte rossa dello spigolo giace sulla faccia arancione (d=2) mentre la parte bianca giace sulla faccia verde (d=1). In totale si avr poter posizionare lo spigolo;

dtot = 2 + 1 = 3

mosse necessarie per

Figura 4.8. Esempio dello spigolo rosso-bianco che dista tre mosse dalla posizione corretta.

4 mosse:

in Figura 4.9 rappresenta l'ultima congurazione possibile ed quella in cui Questa l'unica congurazione

entrambi i colori dello spigolo giacciono su facce opposte.

16

4  Implementazione dell'algoritmo A*

che si risolve in quattro mosse. Infatti la faccia gialla dista 2 dalla faccia bianca e la faccia arancione dista 2 dalla faccia rossa

dtot = 2 + 2 = 4

mosse necessarie.

Figura 4.9. Esempio dello spigolo rosso-bianco che dista quattro mosse dalla posizione corretta.

La somma delle distanze

dtot

calcolate per ogni spigolo bianco fornisce la stima euristica

(n), h

la

quale pu essere dunque sintetizzata come segue:

1. individuare uno spigolo in cui uno dei due colori bianco;

2. individuare il colore della faccia su cui giace il bianco individuato nel punto uno;

3. calcolare la distanza tra la faccia su cui giace il bianco e la faccia bianca;

4. calcolare la distanza tra la faccia su cui giace la parte non bianca dello spigolo e la faccia del colore non bianco dello spigolo;

5. sommare le distanze calcolate;

6. se

dtot = 1+1 e lo spigolo giace sulla linea bianco - colore_spigolo - giallo - colore_opposto_spigolo dtot = 1 + 1 + 1

7. ripetere dal punto 1 per ogni spigolo bianco;

8. calcolare

(n) h

come

(n) = dtot1 + dtot2 + dtot3 + dtot4 . h

4.2

Riconoscimento delle simmetrie

Nello sviluppo dell'algoritmo A* sar necessario individuare se delle congurazioni sono gi state elaborate. Per fare questo non suciente un semplice controllo nel quale si verica se le sei matrici con cui rappresentato il cubo siano identiche alle sei matrici di una congurazione gi analizzata in precedenza. Infatti, possibile che le varie mosse che sono eseguite portino a congurazioni Inoltre la posizione degli angoli

equivalenti ma rappresentate dierentemente nelle sei matrici. ininuente.

Ad esempio in Figura 4.10 sono mostrate quattro congurazioni completamente

identiche dal punto di vista della croce. Le funzioni che nel programma C si occupano di individuare queste simmetrie sono:

17

4  Implementazione dell'algoritmo A*

Figura 4.10. Quattro congurazioni equivalenti dal punto di vista della croce.
elementoLista* esiste(...); int confronta(...);

4.3

Implementazione dell'algoritmo

L'algoritmo diviso nelle seguenti fasi:

1. Il nodo di partenza (start), che corrisponde al cubo mischiato, posto all'interno di una lista di nome OPEN dopo averne calcolato la funzione di valutazione

(start); f (start) = 0 + h

//passo 1 insert(start,g+h(&start),NULL,NULL);
2. si controlla se la lista OPEN vuota, in quel caso si esce con fallimento;

while(1){ //passo 2 if(open==NULL){ printf("Fallimento: open vuota\n"); return -1; } ... }

18

4  Implementazione dell'algoritmo A*

3. si rimuove il primo nodo dalla lista OPEN la quale ordinata in modo crescente rispetto al valore della funzione di valutazione f(n) e lo si pone in una lista di nome CLOSED;

//passo 3 ptr = pop(); append(ptr);


4. si controlla se il nodo appena inserito un nodo nale e in quel caso si esce con il cammino risolutivo trovato;

//passo 4 if(h(&ptr->c) == 0){ //condizione finale printf("Successo.\n"); percorso(ptr); return 0; }


5. si espande il nodo considerato generando tutti i suoi successori, ossia si creano 18 cubi ognuno dei quali rappresenta una possibile mossa eettuata sul cubo di partenza. Per ognuno dei cubi generati si calcola la funzione di valutazione;

//passo 5 g = hop(ptr); n[0] = r(&ptr->c); f_n[0] = g + h(&n[0]); n[1] = r1(&ptr->c); f_n[1] = g + h(&n[1]); n[2] = l(&ptr->c); f_n[2] = g + h(&n[2]); n[3] = l1(&ptr->c); f_n[3] = g + h(&n[3]); n[4] = u(&ptr->c); f_n[4] = g + h(&n[4]); n[5] = u1(&ptr->c); f_n[5] = g + h(&n[5]); n[6] = d(&ptr->c); f_n[6] = g + h(&n[6]); n[7] = d1(&ptr->c); f_n[7] = g + h(&n[7]);

19

4  Implementazione dell'algoritmo A*

n[8] = f(&ptr->c); f_n[8] = g + h(&n[8]); n[9] = f1(&ptr->c); f_n[9] = g + h(&n[9]); n[10] = b(&ptr->c); f_n[10] = g + h(&n[10]); n[11] = b1(&ptr->c); f_n[11] = g + h(&n[11]); n[12] = m(&ptr->c); f_n[12] = g + h(&n[12]); n[13] = m1(&ptr->c); f_n[13] = g + h(&n[13]); n[14] = e(&ptr->c); f_n[14] = g + h(&n[14]); n[15] = e1(&ptr->c); f_n[15] = g + h(&n[15]); n[16] = s(&ptr->c); f_n[16] = g + h(&n[16]); n[17] = s1(&ptr->c); f_n[17] = g + h(&n[17]);
6. ad ogni nodo che non si trova n in OPEN n in CLOSED (si veda 4.2 Riconoscimento delle simmetrie) si associa la rispettiva funzione di valutazione, si creano i puntatori al nodo che li ha generati (padre) e si inseriscono in modo ordinato nella lista OPEN;

for (i=0;i<18;i++){ //passo 6 esisteInOpen = esiste(&n[i],open); esisteInClosed = esiste(&n[i],closed); if(esisteInOpen == NULL && esisteInClosed == NULL) insert(n[i], f_n[i], ptr, mossa[i]); ... }
7. nel caso in cui un nodo appena generato corrisponda ad una congurazione gi analizzata e quindi presente in OPEN o in CLOSED (si veda 4.2), si verica se la funzione di valutazione del nodo in questione sia minore di quella gi presente. In tal caso se il nodo era nella lista CLOSED si aggiorna il valore di f, si reindirizza il puntatore al padre che ha generato il nodo con la funzione di valutazione inferiore e si sposta l'elemento aggiornato nella lista OPEN;

20

4  Implementazione dell'algoritmo A*

se il nodo, invece, era nella lista OPEN necessario solamente aggiornare la funzione di valutazione e il puntatore al nodo padre;

for (i=0;i<18;i++){ ... //passo 7 else{ if(esisteInOpen != NULL && f_n[i] < esisteInOpen->f){ insert(n[i], f_n[i], ptr, mossa[i]); elimina(open,esisteInOpen); } if(esisteInClosed != NULL && f_n[i] < esisteInClosed->f){ insert(n[i], f_n[i], ptr, mossa[i]); elimina(closed,esisteInClosed); } } }
8. si continua l'algoritmo a partire dal passo numero 2.

21

Capitolo 5

Applicazione dell'algoritmo
In questo capitolo saranno analizzati alcuni esempi dell'applicazione dell'algoritmo A*.

In Figura 5.1 mostrato un semplice esempio in cui la congurazione iniziale mischiata ruotando di 180 la faccia frontale. Questo fa si che la risoluzione della croce, applicando l'algoritmo descritto nei capitoli precedenti, presenti un albero come ragurato in Figura 5.1.

Figura 5.1. Albero delle soluzioni.


possibile notare come l'euristica suggerisca da subito le mosse che permettono la risoluzione nel minor numero di passi, indicando le mosse f e f ' come nodi pi promettenti. In Figura 5.2 mostrato l'output del programma C che implementa l'algoritmo.

22

5  Applicazione dell'algoritmo

Figura 5.2. Output del programma rubik.c.


Di seguito mostrata l'applicazione dell'algoritmo su un esempio pi complesso dove il cubo iniziale mischiato ragurato in Figura 5.3

Figura 5.3. Congurazione 2D di un cubo a seguito di uno scramble.

23

5  Applicazione dell'algoritmo

La soluzione fornita dal programma rappresentata in Figura 5.4.

Figura 5.4. Output del programma rubik.c. a seguito di uno scramble.


Si pu notare dalla stampa in Figura 5.4 che il numero di nodi memorizzati decisamente basso: 209 contro i 648.232.975 che avrebbe richiesto una ricerca in ampiezza standard. Inne abbiamo analizzato le prestazioni dell'algoritmo facendogli eseguire 10.000 cubi completamente mischiati ottenendo i risultati illustrati in Figura 5.5;

Figura 5.5. Risultati su 10.000 cubi diversi.


da cui si pu calcolare che mediamente l'algoritmo impiega 6,54 mosse per risolvere la croce bianca a partire da una qualsiasi congurazione.

24

Capitolo 6

Codice sorgente
#include <stdio.h> #include <stdlib.h> #include <string.h> #define PESI 0 #define NO_PESI 1 typedef struct cubo{ char F[3][3]; char R[3][3]; char B[3][3]; char L[3][3]; char D[3][3]; char U[3][3]; } cubo; typedef struct struttura{ cubo c; int f; char m[2]; //mossa struct struttura* padre; struct struttura* next; } elementoLista; cubo r(cubo* c);

25

6  Codice sorgente

cubo l(cubo* c); cubo u(cubo* c); cubo d(cubo* c); cubo f(cubo* c); cubo b(cubo* c); cubo m(cubo* c); cubo e(cubo* c); cubo s(cubo* c); cubo r1(cubo* c); cubo l1(cubo* c); cubo u1(cubo* c); cubo d1(cubo* c); cubo f1(cubo* c); cubo b1(cubo* c); cubo m1(cubo* c); cubo e1(cubo* c); cubo s1(cubo* c); cubo esegui(cubo* c, char* m); void ruota(char m[3][3], char mCopia[3][3]); void ruota1(char m[3][3], char mCopia[3][3]); int hop(elementoLista* item); int h(cubo* c); int dist(char color1, char color2); char opposto(char color); void insert(cubo c, int f, elementoLista* padre, char* m); void append(elementoLista* item); elementoLista* elimina(elementoLista* testa, elementoLista* item); elementoLista* pop(); void stampaListe(); void stampa(cubo* c); void percorso(elementoLista* foglia, int n);

26

6  Codice sorgente

void albero(); elementoLista* esiste(cubo* c, elementoLista* testa); int confronta(char cBianco, char spigolo, char Cspigolo, cubo* c); cubo scramble(cubo* c); elementoLista* open; elementoLista* closed; int nodi_generati(); int main (void){ cubo start; int i,j; elementoLista* esisteInOpen=NULL; elementoLista* esisteInClosed=NULL; elementoLista* ptr; cubo n[18]; int f_n[18]; char mossa[18][3]; int g = 0; int menu = 0; char scelta[3]; FILE *file; open = NULL; closed = NULL; strcpy(mossa[0],"r"); strcpy(mossa[1],"r1"); strcpy(mossa[2],"l"); strcpy(mossa[3],"l1"); strcpy(mossa[4],"u"); strcpy(mossa[5],"u1"); strcpy(mossa[6],"d"); strcpy(mossa[7],"d1");

27

6  Codice sorgente

strcpy(mossa[8],"f"); strcpy(mossa[9],"f1"); strcpy(mossa[10],"b"); strcpy(mossa[11],"b1"); strcpy(mossa[12],"m"); strcpy(mossa[13],"m1"); strcpy(mossa[14],"e"); strcpy(mossa[15],"e1"); strcpy(mossa[16],"s"); strcpy(mossa[17],"s1"); for (i=0;i<3;i++){ for(j=0;j<3;j++){ start.F[i][j]='r'; start.R[i][j]='g'; start.B[i][j]='o'; start.L[i][j]='b'; start.D[i][j]='w'; start.U[i][j]='y'; } } printf("Il programma funziona in tre modalita' differenti.\n"); printf("Digitare:\n"); printf("1 per mischiare il cubo automaticamente\n"); printf("2 per mischiare il cubo manualmente\n"); printf("3 per mischiare il cubo a partire da un elenco di mosse lette da file\n"); scanf("%d",&menu); switch(menu){ case 1: start = scramble(&start); break; case 2: stampa(&start); printf("Digitare una tra le seguenti mosse:\n r, r1, l, l1, u, u1, d, d1, f, f1, b, b1, m, m1, e, e1, s, s1\n\n");

28

6  Codice sorgente

scanf("%s",scelta); while(strcmp(scelta,"x")!=0){ start = esegui(&start, scelta); stampa(&start); printf("Digitare la mossa successiva o digitare x per avviare la risoluzione della croce\n"); scanf("%s",scelta); } break; case 3: file=fopen("scramble.txt","r"); if(!file){ printf("file scramble.txt non trovato.\n"); return -1; } else{ while(!feof(file)){ fscanf(file, "%s", scelta); start=esegui(&start, scelta); } fclose(file); } break; default: printf("scelta non valida, il cubo sara' mischiato automaticamente\n"); start = scramble(&start); break; } printf("Configurazione di partenza:\n"); stampa(&start); //passo 1 insert(start,g+h(&start),NULL,NULL); //passo 2 while(1){

29

6  Codice sorgente

if(open==NULL){ printf("Fallimento: open vuota\n"); return -1; } //passo 3 ptr = pop(); append(ptr); //passo 4 if(h(&ptr->c) == 0){ //condizione finale printf("\nSuccesso\n"); printf("Numero di mosse: %d\n",hop(ptr)-1); percorso(ptr,NO_PESI); printf("\nNodi memorizzati: %d\n",nodi_generati()); printf("Soluzione:\n"); stampa(&ptr->c); printf("\n\nDigitare 1 per vedere i nodi analizzati, con il relativo peso\n"); menu = 0; scanf("%d",&menu); if(menu == 1){ printf("\n\nalbero:\n"); percorso(ptr,PESI); printf("**\n"); albero(); } printf("\n"); system("pause"); return 0; } //passo 5 g = hop(ptr); n[0] = r(&ptr->c); f_n[0] = g + h(&n[0]); n[1] = r1(&ptr->c); f_n[1] = g + h(&n[1]);

30

6  Codice sorgente

n[2] = l(&ptr->c); f_n[2] = g + h(&n[2]); n[3] = l1(&ptr->c); f_n[3] = g + h(&n[3]); n[4] = u(&ptr->c); f_n[4] = g + h(&n[4]); n[5] = u1(&ptr->c); f_n[5] = g + h(&n[5]); n[6] = d(&ptr->c); f_n[6] = g + h(&n[6]); n[7] = d1(&ptr->c); f_n[7] = g + h(&n[7]); n[8] = f(&ptr->c); f_n[8] = g + h(&n[8]); n[9] = f1(&ptr->c); f_n[9] = g + h(&n[9]); n[10] = b(&ptr->c); f_n[10] = g + h(&n[10]); n[11] = b1(&ptr->c); f_n[11] = g + h(&n[11]); n[12] = m(&ptr->c); f_n[12] = g + h(&n[12]); n[13] = m1(&ptr->c); f_n[13] = g + h(&n[13]); n[14] = e(&ptr->c); f_n[14] = g + h(&n[14]); n[15] = e1(&ptr->c); f_n[15] = g + h(&n[15]); n[16] = s(&ptr->c); f_n[16] = g + h(&n[16]); n[17] = s1(&ptr->c); f_n[17] = g + h(&n[17]); for (i=0;i<18;i++){ //passo 6 esisteInOpen = esiste(&n[i],open); esisteInClosed = esiste(&n[i],closed);

31

6  Codice sorgente

if(esisteInOpen == NULL && esisteInClosed == NULL){ // printf("%s ",mossa[i]); insert(n[i], f_n[i], ptr, mossa[i]); } //passo 7 else{ if(esisteInOpen != NULL && f_n[i] < esisteInOpen->f){ insert(n[i], f_n[i], ptr, mossa[i]); elimina(open,esisteInOpen); } if(esisteInClosed != NULL && f_n[i] < esisteInClosed->f){ insert(n[i], f_n[i], ptr, mossa[i]); elimina(closed,esisteInClosed); } } } } return 0; } //restituisce il numero di mosse necessarie per posizionare un singolo pezzo int valuta(char cBianco, char spigolo, char cSpigolo){ int p; //distanza tra il centro del bianco e il centro bianco p = dist(cBianco, 'w'); //distanza tra il centro dello spigolo e il centro di colore dello spigolo p = p + dist(spigolo, cSpigolo); if(p == 2){ //il centro del bianco spigolo o OppostoSpigolo if(cBianco == spigolo || cBianco == opposto(spigolo)){ //il centro di spigolo bianco o giallo? if(cSpigolo == 'w' || cSpigolo == 'y'){ p++; }

32

6  Codice sorgente

} } return p; } int h(cubo* c){ int d = 0; if(c->F[0][1] == 'w'){//trovo il bianco d = d + valuta(c->F[1][1],c->U[2][1],c->U[1][1]); } if(c->F[1][0] == 'w'){//trovo il bianco d = d + valuta(c->F[1][1],c->L[1][2],c->L[1][1]); } if(c->F[1][2] == 'w'){//trovo il bianco d = d + valuta(c->F[1][1],c->R[1][0],c->R[1][1]); } if(c->F[2][1] == 'w'){//trovo il bianco d = d + valuta(c->F[1][1],c->D[0][1],c->D[1][1]); } if(c->R[0][1] == 'w'){//trovo il bianco d = d + valuta(c->R[1][1],c->U[1][2],c->U[1][1]); } if(c->R[1][0] == 'w'){//trovo il bianco d = d + valuta(c->R[1][1],c->F[1][2],c->F[1][1]); } if(c->R[1][2] == 'w'){//trovo il bianco d = d + valuta(c->R[1][1],c->B[1][0],c->B[1][1]); } if(c->R[2][1] == 'w'){//trovo il bianco d = d + valuta(c->R[1][1],c->D[1][2],c->D[1][1]); } if(c->B[0][1] == 'w'){//trovo il bianco d = d + valuta(c->B[1][1],c->U[0][1],c->U[1][1]);

33

6  Codice sorgente

} if(c->B[1][0] == 'w'){//trovo il bianco d = d + valuta(c->B[1][1],c->R[1][2],c->R[1][1]); } if(c->B[1][2] == 'w'){//trovo il bianco d = d + valuta(c->B[1][1],c->L[1][0],c->L[1][1]); } if(c->B[2][1] == 'w'){//trovo il bianco d = d + valuta(c->B[1][1],c->D[2][1],c->D[1][1]); } if(c->L[0][1] == 'w'){//trovo il bianco d = d + valuta(c->L[1][1],c->U[1][0],c->U[1][1]); } if(c->L[1][0] == 'w'){//trovo il bianco d = d + valuta(c->L[1][1],c->B[1][2],c->B[1][1]); } if(c->L[1][2] == 'w'){//trovo il bianco d = d + valuta(c->L[1][1],c->F[1][0],c->F[1][1]); } if(c->L[2][1] == 'w'){//trovo il bianco d = d + valuta(c->L[1][1],c->D[1][0],c->D[1][1]); } if(c->U[0][1] == 'w'){//trovo il bianco d = d + valuta(c->U[1][1],c->B[0][1],c->B[1][1]); } if(c->U[1][0] == 'w'){//trovo il bianco d = d + valuta(c->U[1][1],c->L[0][1],c->L[1][1]); } if(c->U[1][2] == 'w'){//trovo il bianco d = d + valuta(c->U[1][1],c->R[0][1],c->R[1][1]); } if(c->U[2][1] == 'w'){//trovo il bianco d = d + valuta(c->U[1][1],c->F[0][1],c->F[1][1]); }

34

6  Codice sorgente

if(c->D[0][1] == 'w'){//trovo il bianco d = d + valuta(c->D[1][1],c->F[2][1],c->F[1][1]); } if(c->D[1][0] == 'w'){//trovo il bianco d = d + valuta(c->D[1][1],c->L[2][1],c->L[1][1]); } if(c->D[1][2] == 'w'){//trovo il bianco d = d + valuta(c->D[1][1],c->R[2][1],c->R[1][1]); } if(c->D[2][1] == 'w'){//trovo il bianco d = d + valuta(c->D[1][1],c->B[2][1],c->B[1][1]); } return d; } int dist(char color1, char color2){ if(color1 == color2) return 0; if(opposto(color1)==color2) return 2; return 1; } char opposto(char color){ switch (color){ case 'r': return 'o'; case 'g': return 'b'; case 'o': return 'r'; case 'b':

35

6  Codice sorgente

return 'g'; case 'w': return 'y'; case 'y': return 'w'; } } cubo r(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][2] = c->D[0][2]; copia.F[1][2] = c->D[1][2]; copia.F[2][2] = c->D[2][2]; copia.U[0][2] = c->F[0][2]; copia.U[1][2] = c->F[1][2]; copia.U[2][2] = c->F[2][2]; copia.B[0][0] = c->U[2][2]; copia.B[1][0] = c->U[1][2]; copia.B[2][0] = c->U[0][2]; copia.D[0][2] = c->B[2][0]; copia.D[1][2] = c->B[1][0];

36

6  Codice sorgente

copia.D[2][2] = c->B[0][0]; ruota(copia.R, c->R); return copia; } cubo r1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][2] = c->U[0][2]; copia.F[1][2] = c->U[1][2]; copia.F[2][2] = c->U[2][2]; copia.U[0][2] = c->B[2][0]; copia.U[1][2] = c->B[1][0]; copia.U[2][2] = c->B[0][0]; copia.B[0][0] = c->D[2][2]; copia.B[1][0] = c->D[1][2]; copia.B[2][0] = c->D[0][2]; copia.D[0][2] = c->F[0][2]; copia.D[1][2] = c->F[1][2]; copia.D[2][2] = c->F[2][2];

37

6  Codice sorgente

ruota1(copia.R, c->R); return copia; } cubo l(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][0] = c->U[0][0]; copia.F[1][0] = c->U[1][0]; copia.F[2][0] = c->U[2][0]; copia.U[0][0] = c->B[2][2]; copia.U[1][0] = c->B[1][2]; copia.U[2][0] = c->B[0][2]; copia.B[0][2] = c->D[2][0]; copia.B[1][2] = c->D[1][0]; copia.B[2][2] = c->D[0][0]; copia.D[0][0] = c->F[0][0]; copia.D[1][0] = c->F[1][0]; copia.D[2][0] = c->F[2][0]; ruota(copia.L, c->L);

38

6  Codice sorgente

return copia; } cubo l1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][0] = c->D[0][0]; copia.F[1][0] = c->D[1][0]; copia.F[2][0] = c->D[2][0]; copia.U[0][0] = c->F[0][0]; copia.U[1][0] = c->F[1][0]; copia.U[2][0] = c->F[2][0]; copia.B[0][2] = c->U[2][0]; copia.B[1][2] = c->U[1][0]; copia.B[2][2] = c->U[0][0]; copia.D[0][0] = c->B[2][2]; copia.D[1][0] = c->B[1][2]; copia.D[2][0] = c->B[0][2]; ruota1(copia.L, c->L); return copia; }

39

6  Codice sorgente

cubo u(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][0] = c->R[0][0]; copia.F[0][1] = c->R[0][1]; copia.F[0][2] = c->R[0][2]; copia.R[0][0] = c->B[0][0]; copia.R[0][1] = c->B[0][1]; copia.R[0][2] = c->B[0][2]; copia.L[0][0] = c->F[0][0]; copia.L[0][1] = c->F[0][1]; copia.L[0][2] = c->F[0][2]; copia.B[0][0] = c->L[0][0]; copia.B[0][1] = c->L[0][1]; copia.B[0][2] = c->L[0][2]; ruota(copia.U, c->U); return copia; } cubo u1(cubo* c){

40

6  Codice sorgente

cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][0] = c->L[0][0]; copia.F[0][1] = c->L[0][1]; copia.F[0][2] = c->L[0][2]; copia.R[0][0] = c->F[0][0]; copia.R[0][1] = c->F[0][1]; copia.R[0][2] = c->F[0][2]; copia.B[0][0] = c->R[0][0]; copia.B[0][1] = c->R[0][1]; copia.B[0][2] = c->R[0][2]; copia.L[0][0] = c->B[0][0]; copia.L[0][1] = c->B[0][1]; copia.L[0][2] = c->B[0][2]; ruota1(copia.U, c->U); return copia; } cubo d(cubo* c){ cubo copia;

41

6  Codice sorgente

int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[2][0] = c->L[2][0]; copia.F[2][1] = c->L[2][1]; copia.F[2][2] = c->L[2][2]; copia.R[2][0] = c->F[2][0]; copia.R[2][1] = c->F[2][1]; copia.R[2][2] = c->F[2][2]; copia.B[2][0] = c->R[2][0]; copia.B[2][1] = c->R[2][1]; copia.B[2][2] = c->R[2][2]; copia.L[2][0] = c->B[2][0]; copia.L[2][1] = c->B[2][1]; copia.L[2][2] = c->B[2][2]; ruota(copia.D, c->D); return copia; } cubo d1(cubo* c){ cubo copia; int i,j;

42

6  Codice sorgente

for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[2][0] = c->R[2][0]; copia.F[2][1] = c->R[2][1]; copia.F[2][2] = c->R[2][2]; copia.R[2][0] = c->B[2][0]; copia.R[2][1] = c->B[2][1]; copia.R[2][2] = c->B[2][2]; copia.L[2][0] = c->F[2][0]; copia.L[2][1] = c->F[2][1]; copia.L[2][2] = c->F[2][2]; copia.B[2][0] = c->L[2][0]; copia.B[2][1] = c->L[2][1]; copia.B[2][2] = c->L[2][2]; ruota1(copia.D, c->D); return copia; } cubo f(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){

43

6  Codice sorgente

copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.R[0][0] = c->U[2][0]; copia.R[1][0] = c->U[2][1]; copia.R[2][0] = c->U[2][2]; copia.L[0][2] = c->D[0][0]; copia.L[1][2] = c->D[0][1]; copia.L[2][2] = c->D[0][2]; copia.D[0][0] = c->R[2][0]; copia.D[0][1] = c->R[1][0]; copia.D[0][2] = c->R[0][0]; copia.U[2][0] = c->L[2][2]; copia.U[2][1] = c->L[1][2]; copia.U[2][2] = c->L[0][2]; ruota(copia.F, c->F); return copia; } cubo f1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j];

44

6  Codice sorgente

copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.R[0][0] = c->D[0][2]; copia.R[1][0] = c->D[0][1]; copia.R[2][0] = c->D[0][0]; copia.L[0][2] = c->U[2][2]; copia.L[1][2] = c->U[2][1]; copia.L[2][2] = c->U[2][0]; copia.D[0][0] = c->L[0][2]; copia.D[0][1] = c->L[1][2]; copia.D[0][2] = c->L[2][2]; copia.U[2][0] = c->R[0][0]; copia.U[2][1] = c->R[1][0]; copia.U[2][2] = c->R[2][0]; ruota1(copia.F, c->F); return copia; } cubo b(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j];

45

6  Codice sorgente

copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.R[0][2] = c->D[2][2]; copia.R[1][2] = c->D[2][1]; copia.R[2][2] = c->D[2][0]; copia.L[0][0] = c->U[0][2]; copia.L[1][0] = c->U[0][1]; copia.L[2][0] = c->U[0][0]; copia.D[2][0] = c->L[0][0]; copia.D[2][1] = c->L[1][0]; copia.D[2][2] = c->L[2][0]; copia.U[0][0] = c->R[0][2]; copia.U[0][1] = c->R[1][2]; copia.U[0][2] = c->R[2][2]; ruota(copia.B, c->B); return copia; } cubo b1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j];

46

6  Codice sorgente

} } copia.R[0][2] = c->U[0][0]; copia.R[1][2] = c->U[0][1]; copia.R[2][2] = c->U[0][2]; copia.L[0][0] = c->D[2][0]; copia.L[1][0] = c->D[2][1]; copia.L[2][0] = c->D[2][2]; copia.D[2][0] = c->R[2][2]; copia.D[2][1] = c->R[1][2]; copia.D[2][2] = c->R[0][2]; copia.U[0][0] = c->L[2][0]; copia.U[0][1] = c->L[1][0]; copia.U[0][2] = c->L[0][0]; ruota1(copia.B, c->B); return copia; } cubo m(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } }

47

6  Codice sorgente

copia.F[0][1] = c->U[0][1]; copia.F[1][1] = c->U[1][1]; copia.F[2][1] = c->U[2][1]; copia.U[0][1] = c->B[2][1]; copia.U[1][1] = c->B[1][1]; copia.U[2][1] = c->B[0][1]; copia.B[0][1] = c->D[2][1]; copia.B[1][1] = c->D[1][1]; copia.B[2][1] = c->D[0][1]; copia.D[0][1] = c->F[0][1]; copia.D[1][1] = c->F[1][1]; copia.D[2][1] = c->F[2][1]; return copia; } cubo m1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[0][1] = c->D[0][1]; copia.F[1][1] = c->D[1][1]; copia.F[2][1] = c->D[2][1];

48

6  Codice sorgente

copia.U[0][1] = c->F[0][1]; copia.U[1][1] = c->F[1][1]; copia.U[2][1] = c->F[2][1]; copia.B[0][1] = c->U[2][1]; copia.B[1][1] = c->U[1][1]; copia.B[2][1] = c->U[0][1]; copia.D[0][1] = c->B[2][1]; copia.D[1][1] = c->B[1][1]; copia.D[2][1] = c->B[0][1]; return copia; } cubo e(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[1][0] = c->L[1][0]; copia.F[1][1] = c->L[1][1]; copia.F[1][2] = c->L[1][2]; copia.R[1][0] = c->F[1][0]; copia.R[1][1] = c->F[1][1]; copia.R[1][2] = c->F[1][2];

49

6  Codice sorgente

copia.B[1][0] = c->R[1][0]; copia.B[1][1] = c->R[1][1]; copia.B[1][2] = c->R[1][2]; copia.L[1][0] = c->B[1][0]; copia.L[1][1] = c->B[1][1]; copia.L[1][2] = c->B[1][2]; return copia; } cubo e1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.F[1][0] = c->R[1][0]; copia.F[1][1] = c->R[1][1]; copia.F[1][2] = c->R[1][2]; copia.R[1][0] = c->B[1][0]; copia.R[1][1] = c->B[1][1]; copia.R[1][2] = c->B[1][2]; copia.L[1][0] = c->F[1][0]; copia.L[1][1] = c->F[1][1]; copia.L[1][2] = c->F[1][2];

50

6  Codice sorgente

copia.B[1][0] = c->L[1][0]; copia.B[1][1] = c->L[1][1]; copia.B[1][2] = c->L[1][2]; return copia; } cubo s(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.R[0][1] = c->U[1][0]; copia.R[1][1] = c->U[1][1]; copia.R[2][1] = c->U[1][2]; copia.L[0][1] = c->D[1][0]; copia.L[1][1] = c->D[1][1]; copia.L[2][1] = c->D[1][2]; copia.D[1][0] = c->R[2][1]; copia.D[1][1] = c->R[1][1]; copia.D[1][2] = c->R[0][1]; copia.U[1][0] = c->L[2][1]; copia.U[1][1] = c->L[1][1]; copia.U[1][2] = c->L[0][1]; return copia;

51

6  Codice sorgente

} cubo s1(cubo* c){ cubo copia; int i,j; for (i=0;i<3;i++){ for(j=0;j<3;j++){ copia.F[i][j]=c->F[i][j]; copia.R[i][j]=c->R[i][j]; copia.B[i][j]=c->B[i][j]; copia.L[i][j]=c->L[i][j]; copia.D[i][j]=c->D[i][j]; copia.U[i][j]=c->U[i][j]; } } copia.R[0][1] = c->D[1][2]; copia.R[1][1] = c->D[1][1]; copia.R[2][1] = c->D[1][0]; copia.L[0][1] = c->U[1][2]; copia.L[1][1] = c->U[1][1]; copia.L[2][1] = c->U[1][0]; copia.D[1][0] = c->L[0][1]; copia.D[1][1] = c->L[1][1]; copia.D[1][2] = c->L[2][1]; copia.U[1][0] = c->R[0][1]; copia.U[1][1] = c->R[1][1]; copia.U[1][2] = c->R[2][1]; return copia; } void ruota (char m[3][3], char mCopia[3][3]){

52

6  Codice sorgente

m[0][0] = mCopia[2][0]; m[0][1] = mCopia[1][0]; m[0][2] = mCopia[0][0]; m[1][0] = mCopia[2][1]; m[1][2] = mCopia[0][1]; m[2][0] = mCopia[2][2]; m[2][1] = mCopia[1][2]; m[2][2] = mCopia[0][2]; } void ruota1 (char m[3][3], char mCopia[3][3]){ m[0][0] = mCopia[0][2]; m[0][1] = mCopia[1][2]; m[0][2] = mCopia[2][2]; m[1][0] = mCopia[0][1]; m[1][2] = mCopia[2][1]; m[2][0] = mCopia[0][0]; m[2][1] = mCopia[1][0]; m[2][2] = mCopia[2][0]; } void stampa(cubo* c){ int i,j; for (i=0;i<3;i++){ printf("\t"); for(j=0;j<3;j++){ printf("%c ",c->U[i][j]); } printf("\n"); } printf("\n"); for (i=0;i<3;i++){ for(j=0;j<3;j++){ printf("%c ",c->L[i][j]);

53

6  Codice sorgente

} printf("\t"); for(j=0;j<3;j++){ printf("%c ",c->F[i][j]); } printf("\t"); for(j=0;j<3;j++){ printf("%c ",c->R[i][j]); } printf("\t"); for(j=0;j<3;j++){ printf("%c ",c->B[i][j]); } printf("\n"); } printf("\n"); for (i=0;i<3;i++){ printf("\t"); for(j=0;j<3;j++){ printf("%c ",c->D[i][j]); } printf("\n"); } } void insert(cubo c, int f, elementoLista* padre, char* m){ elementoLista* nuovoElemento; elementoLista* indice; elementoLista* before; nuovoElemento=(elementoLista*)malloc(sizeof(elementoLista)); nuovoElemento->c=c; nuovoElemento->f=f; nuovoElemento->next=NULL; nuovoElemento->padre=padre; if(m!=NULL){

54

6  Codice sorgente

strcpy(nuovoElemento->m, m); } if(open == NULL){ open = nuovoElemento; }else{ indice=open; before=indice; if(nuovoElemento->f < indice->f){ nuovoElemento->next = open; open = nuovoElemento; return; } while(indice->next!=NULL){ indice=indice->next; if(nuovoElemento->f < indice->f){ before->next = nuovoElemento; nuovoElemento->next = indice; return; } before=indice; } indice->next=nuovoElemento; } return; } elementoLista* pop(elementoLista* testa){ elementoLista* indice; indice=open; open=open->next; indice->next = NULL; return indice; }

55

6  Codice sorgente

elementoLista* elimina(elementoLista* testa, elementoLista* item){ elementoLista* indice; elementoLista* before; indice=testa; if(item == NULL){ //elimina il primo elemento testa=testa->next; free(indice); return testa; } before=indice; if(item == indice){ testa=testa->next; free(indice); return testa; } while(indice->next!=NULL){ indice=indice->next; if(item == indice){ before->next = indice->next; free(indice); return testa; } before=indice; } } void stampaListe(){ //debug elementoLista* indice = open; printf("----open----\n"); while(indice != NULL){

56

6  Codice sorgente

printf("mossa: %s\nvalore: %d\nindirizzo: %d\nnext: %d\npadre: %d\n configurazione:\n",indice->m,indice->f,indice,indice->next,indice->padre); stampa(&indice->c); indice = indice->next; } printf("------------\n"); printf("----closed----\n"); indice = closed; while(indice != NULL) { printf("mossa: %s\nvalore: %d\nindirizzo: %d\nnext: %d\npadre: %d\n configurazione:\n",indice->m,indice->f,indice,indice->next,indice->padre); stampa(&indice->c); indice = indice->next; } printf("--------------\n"); } void percorso(elementoLista* foglia, int n){ char temp[11][10]; int j; int i = 0; while(foglia->padre != NULL){ //stampa(&foglia->c);printf("\n"); if(n == PESI) sprintf(temp[i], "[%s(%d)] ",foglia->m,foglia->f); else sprintf(temp[i], "[%s] ",foglia->m); foglia = foglia->padre; i++; } for(j= i-1; j >= 0; j--){ printf("%s",temp[j]); if(j>0) printf(" --> "); }

57

6  Codice sorgente

} elementoLista* esiste(cubo* c, elementoLista* testa){ int d = 0; while(testa != NULL) { if(c->F[0][1] == 'w'){//trovo il bianco d = d + confronta(c->F[1][1],c->U[2][1],c->U[1][1], &testa->c); } if(c->F[1][0] == 'w'){//trovo il bianco d = d + confronta(c->F[1][1],c->L[1][2],c->L[1][1], &testa->c); } if(c->F[1][2] == 'w'){//trovo il bianco d = d + confronta(c->F[1][1],c->R[1][0],c->R[1][1], &testa->c); } if(c->F[2][1] == 'w'){//trovo il bianco d = d + confronta(c->F[1][1],c->D[0][1],c->D[1][1], &testa->c); } if(c->R[0][1] == 'w'){//trovo il bianco d = d + confronta(c->R[1][1],c->U[1][2],c->U[1][1], &testa->c); } if(c->R[1][0] == 'w'){//trovo il bianco d = d + confronta(c->R[1][1],c->F[1][2],c->F[1][1], &testa->c); } if(c->R[1][2] == 'w'){//trovo il bianco d = d + confronta(c->R[1][1],c->B[1][0],c->B[1][1], &testa->c); } if(c->R[2][1] == 'w'){//trovo il bianco d = d + confronta(c->R[1][1],c->D[1][2],c->D[1][1], &testa->c); } if(c->B[0][1] == 'w'){//trovo il bianco d = d + confronta(c->B[1][1],c->U[0][1],c->U[1][1], &testa->c); } if(c->B[1][0] == 'w'){//trovo il bianco

58

6  Codice sorgente

d = d + confronta(c->B[1][1],c->R[1][2],c->R[1][1], &testa->c); } if(c->B[1][2] == 'w'){//trovo il bianco d = d + confronta(c->B[1][1],c->L[1][0],c->L[1][1], &testa->c); } if(c->B[2][1] == 'w'){//trovo il bianco d = d + confronta(c->B[1][1],c->D[2][1],c->D[1][1], &testa->c); } if(c->L[0][1] == 'w'){//trovo il bianco d = d + confronta(c->L[1][1],c->U[1][0],c->U[1][1], &testa->c); } if(c->L[1][0] == 'w'){//trovo il bianco d = d + confronta(c->L[1][1],c->B[1][2],c->B[1][1], &testa->c); } if(c->L[1][2] == 'w'){//trovo il bianco d = d + confronta(c->L[1][1],c->F[1][0],c->F[1][1], &testa->c); } if(c->L[2][1] == 'w'){//trovo il bianco d = d + confronta(c->L[1][1],c->D[1][0],c->D[1][1], &testa->c); } if(c->U[0][1] == 'w'){//trovo il bianco d = d + confronta(c->U[1][1],c->B[0][1],c->B[1][1], &testa->c); } if(c->U[1][0] == 'w'){//trovo il bianco d = d + confronta(c->U[1][1],c->L[0][1],c->L[1][1], &testa->c); } if(c->U[1][2] == 'w'){//trovo il bianco d = d + confronta(c->U[1][1],c->R[0][1],c->R[1][1], &testa->c); } if(c->U[2][1] == 'w'){//trovo il bianco d = d + confronta(c->U[1][1],c->F[0][1],c->F[1][1], &testa->c); } if(c->D[0][1] == 'w'){//trovo il bianco d = d + confronta(c->D[1][1],c->F[2][1],c->F[1][1], &testa->c);

59

6  Codice sorgente

} if(c->D[1][0] == 'w'){//trovo il bianco d = d + confronta(c->D[1][1],c->L[2][1],c->L[1][1], &testa->c); } if(c->D[1][2] == 'w'){//trovo il bianco d = d + confronta(c->D[1][1],c->R[2][1],c->R[1][1], &testa->c); } if(c->D[2][1] == 'w'){//trovo il bianco d = d + confronta(c->D[1][1],c->B[2][1],c->B[1][1], &testa->c); } if(d == 4) return testa; d=0; testa = testa->next; } return NULL; } int confronta(char cBianco, char spigolo, char Cspigolo, cubo* c){ if(c->F[0][1] == 'w'){//trovo il bianco if(c->F[1][1] == cBianco && c->U[2][1] == spigolo && c->U[1][1] == Cspigolo) return 1; } if(c->F[1][0] == 'w'){//trovo il bianco if(c->F[1][1] == cBianco && c->L[1][2] == spigolo && c->L[1][1] == Cspigolo) return 1; } if(c->F[1][2] == 'w'){//trovo il bianco if(c->F[1][1] == cBianco && c->R[1][0] == spigolo && c->R[1][1] == Cspigolo) return 1; } if(c->F[2][1] == 'w'){//trovo il bianco if(c->F[1][1] == cBianco && c->D[0][1] == spigolo && c->D[1][1] == Cspigolo) return 1; }

60

6  Codice sorgente

if(c->R[0][1] == 'w'){//trovo il bianco if(c->R[1][1] == cBianco && c->U[1][2] == spigolo && c->U[1][1] == Cspigolo) return 1; } if(c->R[1][0] == 'w'){//trovo il bianco if(c->R[1][1] == cBianco && c->F[1][2] == spigolo && c->F[1][1] == Cspigolo) return 1; } if(c->R[1][2] == 'w'){//trovo il bianco if(c->R[1][1] == cBianco && c->B[1][0] == spigolo && c->B[1][1] == Cspigolo) return 1; } if(c->R[2][1] == 'w'){//trovo il bianco if(c->R[1][1] == cBianco && c->D[1][2] == spigolo && c->D[1][1] == Cspigolo) return 1; } if(c->B[0][1] == 'w'){//trovo il bianco if(c->B[1][1] == cBianco && c->U[0][1] == spigolo && c->U[1][1] == Cspigolo) return 1; } if(c->B[1][0] == 'w'){//trovo il bianco if(c->B[1][1] == cBianco && c->R[1][2] == spigolo && c->R[1][1] == Cspigolo) return 1; } if(c->B[1][2] == 'w'){//trovo il bianco if(c->B[1][1] == cBianco && c->L[1][0] == spigolo && c->L[1][1] == Cspigolo) return 1; } if(c->B[2][1] == 'w'){//trovo il bianco if(c->B[1][1] == cBianco && c->D[2][1] == spigolo && c->D[1][1] == Cspigolo) return 1; } if(c->L[0][1] == 'w'){//trovo il bianco if(c->L[1][1] == cBianco && c->U[1][0] == spigolo && c->U[1][1] == Cspigolo)

61

6  Codice sorgente

return 1; } if(c->L[1][0] == 'w'){//trovo il bianco if(c->L[1][1] == cBianco && c->B[1][2] == spigolo && c->B[1][1] == Cspigolo) return 1; } if(c->L[1][2] == 'w'){//trovo il bianco if(c->L[1][1] == cBianco && c->F[1][0] == spigolo && c->F[1][1] == Cspigolo) return 1; } if(c->L[2][1] == 'w'){//trovo il bianco if(c->L[1][1] == cBianco && c->D[1][0] == spigolo && c->D[1][1] == Cspigolo) return 1; } if(c->U[0][1] == 'w'){//trovo il bianco if(c->U[1][1] == cBianco && c->B[0][1] == spigolo && c->B[1][1] == Cspigolo) return 1; } if(c->U[1][0] == 'w'){//trovo il bianco if(c->U[1][1] == cBianco && c->L[0][1] == spigolo && c->L[1][1] == Cspigolo) return 1; } if(c->U[1][2] == 'w'){//trovo il bianco if(c->U[1][1] == cBianco && c->R[0][1] == spigolo && c->R[1][1] == Cspigolo) return 1; } if(c->U[2][1] == 'w'){//trovo il bianco if(c->U[1][1] == cBianco && c->F[0][1] == spigolo && c->F[1][1] == Cspigolo) return 1; } if(c->D[0][1] == 'w'){//trovo il bianco if(c->D[1][1] == cBianco && c->F[2][1] == spigolo && c->F[1][1] == Cspigolo) return 1; } if(c->D[1][0] == 'w'){//trovo il bianco

62

6  Codice sorgente

if(c->D[1][1] == cBianco && c->L[2][1] == spigolo && c->L[1][1] == Cspigolo) return 1; } if(c->D[1][2] == 'w'){//trovo il bianco if(c->D[1][1] == cBianco && c->R[2][1] == spigolo && c->R[1][1] == Cspigolo) return 1; } if(c->D[2][1] == 'w'){//trovo il bianco if(c->D[1][1] == cBianco && c->B[2][1] == spigolo && c->B[1][1] == Cspigolo) return 1; } return 0; } void append(elementoLista* item){ elementoLista* indice = closed; if(closed == NULL){ closed = item; return; } while(indice->next != NULL){ indice = indice->next; } indice->next = item; } int hop(elementoLista* item){ int i = 0; while(item != NULL){ i++; item = item->padre;

63

6  Codice sorgente

} return i; } void albero(){ elementoLista* indice = open; while(indice != NULL){ percorso(indice,PESI);printf("\n"); indice = indice->next; } } cubo scramble(cubo* c){ int random; int i=0; srand(time(NULL)); for(i = 0; i < 35; i++){ random = rand() % 18; switch(random){ case 0: *c = r(c); break; case 1: *c = r1(c); break; case 2: *c = l(c); break; case 3: *c = l1(c); break; case 4: *c = u(c);

64

6  Codice sorgente

break; case 5: *c = u1(c); break; case 6: *c = d(c); break; case 7: *c = d1(c); break; case 8: *c = f(c); break; case 9: *c = f1(c); break; case 10: *c = b(c); break; case 11: *c = b1(c); break; case 12: *c = m(c); break; case 13: *c = m1(c); break; case 14: *c = e(c); break; case 15: *c = e1(c); break; case 16: *c = s(c); break;

65

6  Codice sorgente

case 17: *c = s1(c); break; } } return *c; } int nodi_generati(){ elementoLista* indice = open; int n = 0; while(indice != NULL){ n++; indice = indice->next; } indice = closed; while(indice != NULL){ n++; indice = indice->next; } return n; } cubo esegui(cubo* c, char* mossa){ if(strcmp(mossa,"r") == 0) return r(c); if(strcmp(mossa,"r1") == 0) return r1(c); if(strcmp(mossa,"l") == 0) return l(c); if(strcmp(mossa,"l1") == 0) return l1(c); if(strcmp(mossa,"u") == 0) return u(c); if(strcmp(mossa,"u1") == 0)

66

6  Codice sorgente

return u1(c); if(strcmp(mossa,"d") == 0) return d(c); if(strcmp(mossa,"d1") == 0) return d1(c); if(strcmp(mossa,"f") == 0) return f(c); if(strcmp(mossa,"f1") == 0) return f1(c); if(strcmp(mossa,"b") == 0) return b(c); if(strcmp(mossa,"b1") == 0) return b1(c); if(strcmp(mossa,"m") == 0) return m(c); if(strcmp(mossa,"m1") == 0) return m1(c); if(strcmp(mossa,"e") == 0) return e(c); if(strcmp(mossa,"e1") == 0) return e1(c); if(strcmp(mossa,"s") == 0) return s(c); if(strcmp(mossa,"s1") == 0) return s1(c); printf("Mossa non valida\n"); return *c; }

67

Bibliograa
http://areeweb.polito.it/didattica/gcia/lucidi/Lucidi_Corso/1_Problem_solving/ problem_solving2.pdf http://it.wikipedia.org/wiki/Cubo_di_rubik http://it.wikipedia.org/wiki/Metodo_Fridrich http://en.wikipedia.org/wiki/A*_search_algorithm

68

You might also like