Sudoku Solver - bugnplay (CH)

Transcript

Sudoku Solver - bugnplay (CH)
!
!
!
Sudoku Solver
Nicolas Lanzetti 4M
Lavoro di maturità
Anno 2012
Liceo Lugano 1
Prof. A. Strupler e Prof. R. Duse
Nicolas Lanzetti
!
Lavoro di maturità
2012
Indice
1. Premessa
Pagina 3
2. Abstract
Pagina 3
3. Introduzione
Pagina 4
Pagina 4
Pagina 5
4. Tesi
Pagina 6
Pagina 6
Pagina 6
Pagina 7
Pagina 8
Pagina 9
Pagina 10
Pagina 11
Pagina 14
Pagina 15
Pagina 16
Pagina 17
Pagina 22
Pagina 26
5. Il sito web
Pagina 27
6. Il concorso
Pagina 27
7. Conclusioni
Pagina 28
8. Ringraziamenti
Pagina 28
9. Fonti
Pagina 29
a. Il Lego Mindstorms
b. Il C++
a. Il Sudoku
i. La storia
ii. Le regole
iii. Come risolvere un Sudoku
iv. Il Sudoku e la matematica
v. Il Sudoku e l’informatica
b. Il robot – La meccanica
c. La programmazione – L’informatica
i. L’inizializzazione e l’impostazione
ii. La lettura dei dati
iii. La risoluzione del Sudoku
iv. La scrittura dei numeri
v. Il task errore
2
Nicolas Lanzetti
!
Lavoro di maturità
2012
1. Premessa
!
Scegliere il proprio lavoro di maturità non è facile; ero indeciso tra alcune proposte,
ma alla fine ho optato, senza mai pentirmene, per la robotica. Sono rimasto quasi
stregato dalla presentazione del professor Strupler ed ero entusiasta di poter creare
qualcosa di mio, diverso da ciò che fanno gli altri, e che andasse un po’ oltre al
semplice pezzo di carta. Questi motivi, oltre alla mia passione per questo genere di
argomenti, mi hanno portato a scegliere il lavoro di maturità di robotica.
Il mio avvicinamento al tema del Sudoku, invece, è avvenuto in modo abbastanza
casuale. Affascinato dai primi mesi di programmazione mi sono lanciato nella costruzione di un software che potesse realizzare i Sudoku. Quando si è trattato di
scegliere cosa costruire, il collegamento è stato rapido.
Il Sudoku, come approfondirò in seguito, è un campo della matematica molto interessante e dove c’è ancora tantissimo da scoprire. L’interazione tra matematica, informatica e meccanica mi attrae particolarmente e mi ha portato alla costruzione di
questo robot.
Ci tengo infine a sottolineare che sia la parte teorica, sia la costruzione del robot,
sia il codice sono integralmente stati pensati e sviluppati da me.
2. Abstract
Il mio lavoro consiste nella costruzione e programmazione di un robot che legge,
risolve e completa un Sudoku. Infatti, dopo aver scandito il Sudoku i cui numeri sono sostituiti da colori, la macchina risolve velocemente il rompicapo e completa le
caselle mancanti scrivendo i rispettivi numeri.
3
Nicolas Lanzetti
!
Lavoro di maturità
2012
3. Introduzione
La mia introduzione al lavoro è piuttosto semplice e si rifà a quanto ho scritto in
precedenza.
Tutto il lavoro è stato realizzato dopo un blocco di dieci lezioni di C++, un linguaggio di programmazione. Il robot, tuttavia, è stato programmato in un linguaggio
chiamato NXC (“not exactly c”) supportato dal software Bricx Command Center. La
parte meccanica è invece interamente realizzata in Lego e al suo pacchetto Lego
Mindstorms o NXT.
a) Il Lego Mindstorms
Il LEGO Mindstorms è un kit robotico fabbricato dall’azienda danese Lego. Si tratta
di un processore, chiamato NXT, programmabile sia tramite un programma elaborato dalla stessa Lego, sia da programmi rilasciati da terzi. L’NXT vanta un processore
a 48 MHz, con 256 KB di memoria flash e 64 KB di RAM, mentre lo schermo LCD è
dotato di 100x64 pixel. Il kit viene venduto con quattro sensori (sensore di tatto, di
vista, di suono e di luce) e tre servo motori. Altri sensori, come il giroscopio o il termometro, sono venduti separatamente.
A sinistra è raffigurato un NXT (computer centrale), mentre a destra si può vedere un esempio di robot costruito con il kit robotico Lego
Mindstorms.
4
Nicolas Lanzetti
!
Lavoro di maturità
2012
b) Il C++
Nella prima parte dell’anno, da gennaio a giugno, abbiamo sfruttato le due ore del
LAM per imparare un linguaggio di programmazione, senza il quale sarebbe quasi
impossibile programmare un robot. Il linguaggio scelto è stato il C++, molto simile
a quello che ci ha poi permesso di interagire con l’NXT. Ovviamente in una decina
di lezione è impensabile studiare a fondo un linguaggio complesso come il C++,
ma è sicuramente possibile acquisire le funzioni più importanti. Nel seguente elenco
passerò in rassegna tutte le principali strutture da noi trattate:
• If, if else e else
• For
• While
• Vettori, matrici e puntatori
Il robot è stato quindi programmato usando principalmente queste strutture: ovviamente la sintassi è importante, ma fondamentali sono il ragionamento e la logica
che portano a scrivere un determinato programma. Alcune strutture supplementari,
come quella che calcola il tempo di esecuzione, possono essere facilmente reperite
sul web o nei libri.
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
5
Nicolas Lanzetti
!
Lavoro di maturità
2012
4. Tesi
a) Il Sudoku
Il Sudoku è un gioco di logica di origini giapponesi, le cui prime pubblicazioni risalgono al XX secolo dopo Cristo. Il giocatore si trova confrontato con una griglia di 9
righe e 9 colonne, suddivisa in quadrati 3x3 (come mostrato nell’immagine) le cui
celle possono contenere un numero da 1 a 9 oppure essere vuote. Il giocatore riceve il Sudoku con solamente alcune celle compilate e, sapendo che in ogni riga, colonna e griglia 3x3 devono esserci tutti i numeri da 1 a 9, deve riuscire a completare
tutti gli spazi vuoti.
Un esempio
di Sudoku.
!
i. La storia
Il primo Sudoku vero e proprio, come indicato precedentemente, risale al XX secolo. L’idea di quadrato o di griglia ha tuttavia un’origine molto più antica di quella
del Sudoku. Esso, infatti, deriva con tutta probabilità dal quadrato magico1 e, in
tempi più recenti, da quesiti posti da matematici appassionati di questo genere di
giochi. Nel 1694 il matematico francese Jacques Ozanam2 formulò un problema
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1
!Il quadrato magico è una griglia quadrata di n lati che deve essere completata con i numeri da 1 a
n! , in modo che la somma dei numeri di ogni riga, colonna e diagonale sia costante. L’esempio più
classico è il quadrato magico 3x3.
2
Jacques Ozanam è stato un matematico francese nato nel 1640 e morto nel 1718.
6
Nicolas Lanzetti
!
Lavoro di maturità
2012
molto vicino al concetto contemporaneo di Sudoku: egli voleva posizionare in una
griglia 4x4 le carte di corte (re, regine e fanti) e gli assi in modo che in nessuna fila o
colonna si presentassero due carte dello stesso seme o valore. Ozanam potrebbe
quindi essere considerato l’inventore del precursore del Sudoku. Poco meno di cento anni più tardi, un altro grandissimo intellettuale si interessò al problema presentato dal collega. Il matematico Leonhard Euler3 diceva al giocatore di considerare
sei reggimenti di sei soldati (dove ogni reggimento ha un determinato colore e ogni
soldato un certo grado) e di disporre i 36 soldati su una griglia 6x6, in modo tale
che in ogni riga o colonna non ci siano soldati dello stesso grado o dello stesso
reggimento. Euler sosteneva non esistesse una soluzione al suo problema sia relativamente a una griglia 6x6, come poi dimostrato nel 1901, sia per griglie di 10x10,
14x14, 18x18 e così via. In questo caso fu però dimostrato, con l’ausilio di un computer, che Euler si sbagliava. Il rompicapo introdotto dal matematico svizzero è oggigiorno chiamato quadrato greco-latino e al posto dei soldatini e dei reggimenti si
utilizzano le lettere latine e quelle greche.
Il Sudoku vero e proprio ha però regole diverse da quelle introdotte da Euler e
Ozanam: bisogna infatti attendere gli anni ’70 del Novecento per la sua invenzione.
Dopo alcune versioni apparse su giornali francesi al termine del XIX secolo, un architetto americano di nome Howard Garns pubblicò nel 1979 quello che lui denominò Number Place, oggi chiamato Sudoku. Qualche anno più tardi il rompicapo fu
introdotto anche in Giappone, paese al quale si deve anche il suo attuale nome:
Sudoku è infatti l’abbreviazione della proposizione “i numeri devono essere da soli”. La consacrazione definitiva arriva nel 2004, quando importanti giornali come
“The Times” e “The New York Times” cominciarono la pubblicazione di Sudoku. Da
qui la diffusione è stata rapida, tanto che qualche anno più tardi il puzzle matematico è diventato un “tormentone estivo” e oggi è uno dei rompicapi più comuni.
ii. Le regole
Il Sudoku presenta regole facili da capire ma difficili da applicare. Bisogna completare la griglia 9x9 con i numeri da 1 a 9 in modo che in ogni riga, ogni colonna e
ogni quadrante 3x3 (sono 9) non si ripeta lo stesso numero. Il giocatore riceve il Sudoku con dei numeri già inseriti e deve provare a completare tutte le restanti caselle. Un recente studio ha dimostrato che Sudoku deve fornire come minimo 17 valori
iniziali per poter essere risolto, anche se, quando le caselle già riempite sono poche, la loro posizione diventa fondamentale al fine della risoluzione del rompicapo.
Dopo l’incredibile diffusione del Sudoku sono subito comparse centinaia di varianti,
come i Sudoku 16x16 o il MiniSudoku4.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Leonhard Euler, poi italianizzato Eulero, è stato un matematico e fisico svizzero, vissuto nel XVIII
secolo. Considerato uno dei più grandi matematici di sempre, contribuì in modo fondamentale allo
sviluppo di diversi ambiti della matematica, come il calcolo infinitesimale e la teoria dei grafi.
4
Il MiniSudoku è una variante del Sudoku, dove il giocatore deve completare una griglia 6x6 alle
stesse regole del classico puzzle ma con quadranti 3x2 anziché 3x3.
3
7
Nicolas Lanzetti
!
Lavoro di maturità
2012
iii. Come risolvere un Sudoku
Si potrebbe scrivere un intero LAM riguardo alle diverse tecniche di risoluzione di
un Sudoku, tuttavia credo sia interessante citare le principali, che, sempre supportate da una buona dose di logica, permettono di completare le 81 celle del rompicapo. Personalmente, grazie a questi trucchetti (che in realtà non vanno studiati, ci si
deve ragionare), riesco a risolvere anche Sudoku di difficoltà Extreme. Ovviamente il
mio modo di procedere è soltanto uno dei tanti: non ne esiste uno ufficiale o uno
migliore, bisogna procedere a seconda della difficoltà del Sudoku, delle proprie
capacità e della propria esperienza.
Un primo passo da seguire consiste nell’escludere tutti i numeri, così da poter già
“trovare” dei numeri. L’esclusione dei numeri permette di trovare tutte le possibili
cifre che possono riempire una determinata cella. Questo procedimento può risultare abbastanza lungo, ma è fondamentale al fine di completare il rompicapo.
L’immagine a fianco mostra come agire: si
escludono i numeri non possibili, scrivendo in
piccolo quelli possibili. In questo caso si può
per esempio già piazzare il numero 8 nella quinta casella della penultima riga.
Una volta piazzati tutti i numeri inizia la fase dove più si deve ragionare. Anche in questo caso
esistono dei piccoli trucchetti, che preferisco
mostrare servendomi di alcuni esempi.
Sempre riferendomi al Sudoku a fianco mi concentro sul settimo quadrato 3x3: posso notare
che nelle prime tre celle devono necessariamente andare i numeri 1, 2 e 8 (tre numeri possibili per tre caselle). Posso quindi escludere questi tre numeri dalle altre celle del quadrato, trovando facilmente che nella
quinta casella ci sarà un 5 e che nella settima ci sarà un 6.
Un altro metodo per trovare numeri “nascosti” è invece applicabile all’ottavo quadrante: il numero 7 andrà per forza nella seconda riga del quadrato (ottava riga del
Sudoku), dunque posso affermare con sicurezza che non ci sarà un 7 nella seconda
riga del settimo e del nono quadrante (malgrado non lo abbia ancora piazzato con
certezza). Ragionando in questo modo si scopre dove si trova il 7 nel nono quadrato
3x3: sarà obbligatoriamente nella terza casella del quadrato, in quanto non può essere piazzato in tutte le altre celle del quadrante (prima colonna e ultima riga sono
escluse a priori, la seconda riga è invece esclusa ragionando come mostrato).
Questi trucchetti permettono dunque di trovare numeri che apparentemente sembrano impossibili da scovare: questo, inoltre, rappresenta anche un limite del computer, che non è in grado di eseguire questi ragionamenti. In alcuni casi anche questo modo di ragionare potrebbe risultare inefficace e sarà dunque necessario trovare altre tecniche di risoluzione, come – nel caso più estremo – il “provare a caso” (si
prova a introdurre un numero malgrado non si sia sicuri che sia quello giusto).
8
Nicolas Lanzetti
!
Lavoro di maturità
2012
iv. Il Sudoku e la matematica
Dire che il Sudoku non ha nulla a che vedere con la matematica è un errore:
l’aritmetica non interviene, dato che il rompicapo è di tipo logico ma è anche di
questa logica che si occupa la matematica. Da un lato si possono scoprire delle curiosità, come calcolare in quanti modi è possibili disporre gli 81 numeri
(5472730538 possibilità5), d’altro lato si può studiare il puzzle e le dinamiche in esso
contenute. In questo campo la matematica va a braccetto con l’informatica, visto il
frequente utilizzo del computer per questo tipo di ricerche.
Più nello specifico, il Sudoku è stato inserito nella categoria dei problemi chiamati
The P vs. NP Problems. La questione P e NP, così definita, fa parte di uno dei sette
greatest unsolved mathematical puzzles of our time6 ed è uno dei maggiori campi
di ricerca contemporanei. Il primo a introdurre questo quesito, malgrado già negli
anni ’60 se ne discutesse, fu Stephen Cook7 nel 1971, che tuttavia non riuscì a dare
una spiegazione soddisfacente. Nei decenni successivi parecchi matematici si interessarono al problema, tanto che oggi una sua dimostrazione verrebbe ricompensata con un milione di dollari.
Un problema in P è un problema la cui soluzione può venire trovata da un computer in tempo polinomiale8, come
per esempio la moltiplicazione o l’addizione. La moltiplicazione di due numeri di n cifre richiede n2 moltiplicazioni:
pensando all’algoritmo di risoluzione in colonna le moltiplicazioni richieste per arrivare al risultato sono sempre il quadrato delle cifre dei
numeri9. Il tempo richiesto da un computer per moltiplicare due numeri sarà quindi
polinomiale, siccome lo è il numero di passaggi per arrivare alla soluzione.
Un problema appartiene invece alla classe NP (non
deterministico a tempo polinomiale) se la sua soluzione è verificabile in tempo polinomiale: un quesito
P è quindi anche un quesito NP, visto che se la sua
soluzione è trovabile in tempo polinomiale è automaticamente verificabile in tempo polinomiale. Bisogna
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
5
Questo numero rappresenta tutti i possibili modi con cui si possono disporre gli 81 numeri del Sudoku tenendo sempre conto delle regole.
6
Keith Delvin, famoso matematico inglese, definisce così i sette problemi matematici all’interno del
suo libro The Millennium Problems.
7
Stephen Cook è un matematico e scienziato dei computer americano nato a Buffalo nel 1934. È
conosciuto per le sue ricerche nella teoria della complessità computazionale.!
8
Con l’espressione in tempo polinomiale si intende che il tempo impiegato da un computer è
esprimibile come un polinomio (somma di potenze di x). ! ! e ln !, per esempio, non sono polinomi.
Il tempo è legato al numero di passaggi necessari per arrivare alla soluzione: se essi sono esprimibili
come polinomio, allora lo è anche il tempo.
9
Moltiplicando 52 per 69 in colonna devo eseguire quattro moltiplicazioni : 9x2, 9x5, 6x2 e 6x5. Il
numero di moltiplicazioni è quindi pari al quadrato delle cifre dei due numeri.
9
Nicolas Lanzetti
!
Lavoro di maturità
2012
tuttavia ancora dimostrare se è vero il contrario: un problema NP è sicuramente un
problema P? Non si sa ancora. Esiste una serie di quesiti che rientrano nella cosiddetta classe NP Complete: un problema p è NP Complete se ogni problema NP
può essere ricondotto a p in tempo polinomiale. Dimostrando dunque che un qualsiasi problema NP Completo appartiene anche a P si troverebbe che P=NP, ossia
che qualsiasi quesito verificabile in tempo polinomiale è anche risolvibile in tempo
polinomiale.
Il Sudoku appartiene proprio alla classe NP Complete ed è per questo che sono in
corso diversi studi: se un algoritmo risolve in tempo polinomiale un Sudoku, allora è
in grado di risolvere qualsiasi problema NP e quindi P=NP. Un altro problema NP
Complete è per esempio la primalità di un numero: se si trovasse un algoritmo in
grado di calcolare numeri primi si dimostrerebbe che P=NP. È quindi chiaro come la
questione P vs. NP ha riscontri in tantissimi ambiti, come l’intelligenza artificiale e la
crittografia. Dimostrando che P=NP cadrebbe infatti gran parte della crittografia
contemporanea, basata appunto su numeri primi fattorizzati da altri numeri primi.
v. Il Sudoku e l’informatica
Lo studio del Sudoku dal punto di vista matematico è stato sempre affiancato dallo
studio a livello informatico. Non bisogna tuttavia confondere le due parti, che, malgrado siano tra loro in relazione, non sono la stessa cosa. La matematica, per esempio, non si occupa della costruzione di algoritmi per la risoluzione del rompicapo,
che, invece, rappresentano un’affascinante sfida per l’informatico.
Da quando il Sudoku ha guadagnato la sua popolarità, sono cominciati a nascere
algoritmi che ne permettevano la risoluzione. Ne sono stati creati moltissimi, anche
di molto complicati, ma, nel seguente paragrafo, ne analizzerò due, che si rifanno a
quanto abbiamo studiato.
L’algoritmo da me creato rientra nella categoria dei cosiddetti backtracking algorithm10: esso si basa su un sistema molto semplice. Prima di tutto considera tutte le
possibili soluzioni al problema e poi le seleziona gradualmente finché non trova
quella corretta. Nel caso del Sudoku, il computer esclude tutti in numeri che non
vanno bene finché non riempie le 81 celle. Il tutto si basa dunque su un sistema ad
esclusione: si escludono i possibili numeri che potrebbero entrare in una cella, finché non si conosce il numero esatto. Una volta ottenuto questo numero lo si riutilizza per scoprirne altri. Si può quindi intuire che questo tipo di algoritmo si basa su
una procedura che viene ripetuta più volte, visto che si utilizzano i risultati trovati
per trovarne altri (nel caso del Sudoku si tratta di numeri).
Una seconda possibilità è rappresentata dall’algoritmo a “forza bruta”, che genera
casualmente soluzioni fino a trovare quella corretta (inventa e poi verifica). Ovvia!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Il termine italiano è “algoritmo torna indietro”, tuttavia in questo genere di argomenti si tende a
utilizzare il termine inglese, visto che il loro studio si sviluppa quasi integralmente nella lingua anglosassone.
10
10
Nicolas Lanzetti
!
Lavoro di maturità
2012
mente questo sistema è meno elegante del primo, ma, se ottimizzato a dovere, può
rivelarsi anche più efficiente (garantisce una soluzione, pur se in tempi relativamente
lenti). Ho provato a costruire anche un algoritmo di questo genere: lasciando tutto
al caso (ossia inserendo numeri in modo totalmente casuale) risulta quasi impossibile giungere alla soluzione del rompicapo (il numero di possibilità è 9!"#$%&!!"#$"#%& ,
un numero di circa 50 cifre11). Ottimizzando a dovere, per esempio procedendo a
delle esclusioni, la probabilità di ottenere la soluzione sale notevolmente e di conseguenza scende il tempo computazionale: così facendo anche l’algoritmo a “forza
bruta” può essere un buon sistema per risolvere dei Sudoku.
!
!
b) Il robot – La meccanica
Il robot, che chiamerò Sudoku Solver, è stato costruito, come anticipato, usando dei
pezzi di LEGO, nello specifico il pacchetto NXT LEGO Mindstorms, che include appunto motori, sensori e computer (chiamato proprio NXT). Il robot da me costruito
è stato realizzato in diverse fasi: sono partito con la costruzione di un prototipo che
ho cercato di migliorare gradualmente sotto ogni aspetto. Facendo dei test sono
stato in grado di capire cosa modificare per ottenere una macchina più efficiente
dal punto di vista meccanico.
Inizio descrivendo cosa fa il robot dall’accensione allo spegnimento, come se lo
stessi filmando. Appena si avvia il programma, il robot si allinea, si posizione sulla
prima colonna e comincia l’analisi di tutte le celle della colonna. Una volta letta la
prima colonna, si sposta sulla seconda e analizza quella, e così via per tutte le colonne. I dati presenti sul Sudoku, che come spiegherò saranno sotto forma di colore, vengono memorizzati dal robot, mentre sullo schermo dell’NXT apparirà gradualmente la scansione del rompicapo. Terminata la lettura di tutto il Sudoku, il robot inizia la risoluzione: man mano che trova
numeri, essi compaiono sullo schermo e
vanno a completare il Sudoku. Una volta
completato tutto il Sudoku, il robot procede
alla scrittura dei numeri: anche in questo caso, cominciando dalla prima colonna, completa ogni cella con il rispettivo numero. Infine, dopo aver completato tutto il rompicapo, torna alla posizione di partenza e può
spegnersi.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Il tempo richiesto per calcolare un numero simile di soluzione sarebbe enorme: sicuramente non
impiegabile su un computer normale. In questo caso, infatti, si considera il numero di modi in cui è
possibile piazzare 81 cifre in un Sudoku, senza tenere conto delle sue regole (considerando, per
esempio, anche il caso in cui gli 81 numeri sono tutti uguali: ciò si differenzia dal calcolo di pagina 9). !
11
11
Nicolas Lanzetti
!
Lavoro di maturità
2012
Nella continuazione del lavoro approfondirò ogni singola fase appena descritta,
spiegandone il funzionamento sia livello meccanico sia a livello informatico.
Motore che gestisce la penna
(Output A)
NXT
!
!
Motore che gestisce
il movimento lungo
l’asse X (Output 3)
Braccio meccanico:
sensore di luminosità
e penna (Input 1)
Sensore di tatto
che permette di
gestire gli errori
(Input 2)
Motore che gestisce il
movimento lungo
l’asse Y (Output 2)
!
Il Sudoku Solver utilizza tre motori, un sensore di luminosità e un sensore di tatto.
Due motori permettono il movimento lungo i due assi (asse x e asse y), mentre il
terzo motore si occupa di abbassare o alzare il pennarello grazie al quale il robot
scrive i diversi numeri. Se i motori si occupano del movimento, il sensore di luminosità gestisce tutta la parte di lettura dei dati: esso garantisce il riconoscimento dei
diversi numeri tramite l’ausilio di colori. Ogni numero è associato a una tonalità di
grigio che il sensore è in grado di riconoscere e analizzare. Il Sudoku iniziale necessita dunque di una modifica: anziché introdurre i valori numerici nella griglia, bisogna inserire una certa tonalità di grigio seguendo la funzione sottostante12.
11
!"#"$ !"#$%& = !"#$%_!"#$%&'!(!"#$%&!×!
)
100
In questo modo è possibile tradurre il Sudoku in un linguaggio riconoscibile dal robot. La lettura dei numeri risulta più complicata: sono riuscito a far leggere dei numeri, ma sull’arco di tutta l’identificazione era molto più probabile fare degli errori.
Ho quindi cominciato a sviluppare un programma in grado di analizzare i colori, lasciando da parte la lettura dei numeri, che però potrei sempre riprendere più tardi.
Il secondo sensore, quello di tatto, serve invece alla gestione degli errori: quando il
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
La stampa del Sudoku deve essere effettuata a colori (malgrado essi non ci siano) utilizzando una
stampante a inchiostro.
12
12
Nicolas Lanzetti
Lavoro di maturità
!
2012
robot legge male è sufficiente premere il sensore per richiedere una seconda analisi.
!
!
!
!
!
1!
!
9!
!
!
!
!
6!
!
4!
3!
!
1!
!
!
!
7!
!
9!
7!
4!
6!
!
!
2!
!
!
8!
!
!
!
4!
!
7!
2!
!
2!
6!
!
5!
7!
3!
8!
!
!
7!
!
6!
2!
!
!
!
!
!
6!
9!
5!
8!
!
!
!
!
!
!
1!
7!
!
9!
8!
6!
!
4!
!
8!
!
!
!
!
!
1!
!
!
Nelle due immagini sovrastanti è mostrato lo stesso Sudoku nella versione numerica
e nella versione cromatica.
A!
C!
B!
Nell’immagine a sinistra è invece
raffigurato il robot da un punto
di vista laterale. In questo caso
si può ben notare il braccio (indicato con A), completo di penna e sensore di luminosità, e il
motore che gestisce l’asse Y (B).
Questo motore è attaccato al
braccio e si muove con
quest’ultimo scivolando sulla
struttura di Lego (C).
Il braccio di Lego, come spiegato, si muove su due dimensioni. La sua precisione è
buona, ma non ottimale, specialmente quando c’è un movimento orizzontale nel
momento in cui il braccio è lontano dal corpo centrale. Nella parte di rilevamento
ho ovviato a questo problema in un modo molto semplice: il robot si muove sempre
dalla cella più lontana a quella più vicina lungo l’asse che ho chiamato Y ed esegue
movimenti sull’asse X solo quando il braccio si trova molto vicino al corpo principale
della macchina. Per quanto riguarda la scrittura non si presentano imprecisioni fino
13
Nicolas Lanzetti
!
Lavoro di maturità
2012
alla sesta riga del Sudoku (terza riga dall’alto), mentre nelle ultime tre bisogna regolare il movimento per scrivere ogni singolo numero. Questo tipo di problema è
principalmente dovuto al materiale Lego, che non permette la costruzione di una
struttura rigida: realizzando il tutto in metallo si eviterebbe questo tipo di problema.
!
!
c) La programmazione – L’informatica
La programmazione del NXT - come anticipato - è integralmente realizzata in NXC,
un linguaggio molto simile al C++. Non è un caso che NXC stia semplicemente per
“Not Exactly C”. Il software di programmazione da me usato si chiama Bricx Command Center; ho inoltre istallato un firmware specifico sul NXT13 per usufruire di certe funzioni (come le matrici) non implementate nel firmware originale. Per evitare
problemi con i driver ho anche istallato tutto quanto era in dotazione con il CD del
Lego Mindstorms.
La programmazione del robot si suddivide in cinque parti:
• L’inizializzazione e l’impostazione
• La lettura dei dati
• La risoluzione del sudoku
• La scrittura dei numeri
• Il task errore
Ognuna di queste cinque fasi è ovviamente fondamentale per l’intero progetto e in
realtà non esiste una vera distinzione fra le fasi, visto che il tutto è inglobato in un
unico lungo codice. Tuttavia è più semplice analizzarlo suddividendolo in quattro
parti distinte.
Il seguente diagramma di flusso illustra cosa fa il robot dall’avvio del programma
alla sua fine; in questo modo è possibile avere una rapida visione del codice e del
suo funzionamento. Anche in questa immagine si nota come il compito del robot
possa essere suddiviso in più parti: il primo rettangolo comprende l’inizializzazione,
il secondo la lettura dei dati, il terzo, il quarto e il quinto la risoluzione del Sudoku,
mentre il sesto fa riferimento alla scrittura dei numeri. Il task errore – come si capirà
più avanti – non rientra nel diagramma di flusso.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
13
Il software Bricx Command Center e il firmware sono scaricabili gratuitamente dal sito
www.bricxcc.sourceforge.net.
14
Nicolas Lanzetti
!
Lavoro di maturità
2012
i. L’inizializzazione e l’impostazione
Questa parte del codice è spesso la più ignorata e più noiosa. Si tratta infatti di dichiarare le variabili, di preparare le matrice e di inserire tutte le API14 che garantiscono una piena compatibilità tra quanto si scrive e quanto legge il robot.
In questa sezione della programmazione inserisco anche una procedura che in realtà si ritrova un po’ ovunque: la scrittura dei numeri sullo schermo. Questo può infatti
essere personalizzato, quindi, mentre il robot legge il Sudoku e mentre lo risolve, lo
schermo si aggiorna con i numeri che compaiono
man mano che esegue il compito. Si ha quindi una
rapida visione della soluzione del Sudoku semplicemente osservando lo schermo dell’NXT. Unico
problema: è impossibile rappresentare il rompicapo
in tutte le sue componenti grafiche (griglia 9x9) con
uno schermo LCD (100x64 pixel) che supporta solo
otto righe (8 pixel per riga) e che dunque non permette la scrittura dell’intero Sudoku; manca dunque
la nona riga, che, in altre parole, resta tagliata fuori
dal monitor. Questo problema è quasi impossibile
da risolvere: bisognerebbe lavorare sui singoli pixel, non potendo usare le API preimpostate per la scrittura di numeri. All’interno del
codice la gestione dello schermo si traduce in una procedura chiamata Screen. La
procedura Screen1, invece, mostra le ultime otto righe del Sudoku (anziché le prime
otto), così è possibile controllare che la lettura del rompicapo sia corretta anche
nell’ultima riga e, in un certo senso, ovviare al problema appena menzionato.
Ora riporto alcune righe di codice appartenenti a questa sezione del programma.
Le seguenti righe di codice sono fondamentali per l’esecuzione dell’intero programma e fanno parte, insieme alla dichiarazione di tutte le variabili, di quella parte
da me chiamata “inizializzazione e impostazione”.
Il ciclo for prepara la matrice, in modo che ogni possibilità sia assegnata al rispettivo
valore (per esempio a[colonna][riga][6]=6 alla possibilità sei, per la casella deve esserci il numero sei, così che una volta che il numero 6 è escluso, si eguaglia
a[colonna][riga][6] a zero). Tutte le altre righe di codice servono a impostare i sensori
che utilizza il robot, ad accedere la luce LED del sensore di luminosità e ad azzerare
il tempo di spegnimento automatico. A questo punto l’NXT è pronto per iniziare il
suo lavoro.
!
for (i=0; i < 9; i++) // preparazione della matrice
{
for (j=0; j < 9; j++)
{
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Con il termine API si intendono tutte quelle procedure che permettono l’esecuzione di determinate funzioni all’interno del programma. Il loro compito è di garantire una perfetta sintonia tra hardware e software.!
14
15
Nicolas Lanzetti
!
Lavoro di maturità
2012
for (k=0; k < 11; k++)
{
a[i][j][k]=k; // var k, ogni possibilità della casella
}
}
}
SetSleepTime(0); // disattiva lo spegnimento automatico
SetSensorLight(IN_1); // imposta il sensore luminosità
SetSensorType(IN_1, IN_TYPE_LIGHT_ACTIVE); // accende il led rosso
SetSensorTouch(IN_2);
!
ii. La lettura dei dati
Come anticipato in precedenza, la lettura dei numeri già presenti sul Sudoku avviene mediante il sensore di luminosità. Esso infatti proietta una luce rossa sul foglio e
misura in percentuale a quanto ammonta l’intensità della luce riflessa. Ogni livello di
grigio ha una diversa riflessione ed è dunque possibile riconoscere che tonalità di
grigio si sta analizzando.
I dati delle intensità sono stati ottenuti empiricamente e poi inseriti nella procedura
chiamata Recognition. Questa parte del programma ha quindi richiesto un lavoro di
sperimentazione: ho dovuto rilevare i dati delle intensità dei diversi grigi e capire
come poterli relazionare a un numero15.
Una volta ottenuto il valore dal sensore (che in realtà è la media di tre misurazioni), il
robot lo confronta con quelli ottenuti da me sperimentalmente e inseriti nel programma, per definire il numero che occupa la rispettiva casella. Dal punto di vista
informatico si tratta di un codice abbastanza semplice, composto da poche variabili
e parecchi if o if else. Le seguenti righe di codice mostrano appunto come il robot
riesce a determinare il numero di una casella.
Il seguente ciclo for gestisce tutto il movimento del braccio robotico e ad ogni cella
rileva la luce riflessa, memorizzando il valore in una variabile chiamata b. Questo valore, tramite una procedura chiamata Recognition, determina il numero della relativa casella.
!
for (i=0; i < 9; i++)
// movimento orizzontale
{
for (j=0; j < 9; j++) // movimento verticale
{
b=0; // inizializzazione, ogni casella ha colori diversi
Wait(500);
b1[0]=Sensor(IN_1); // prende il valore della casella
Wait(50);
b1[1]=Sensor(IN_1);
Wait(500);
b1[2]=Sensor(IN_1);
b=((b1[0]+ b1[1]+ b1[2])/3)+0.5; // fa la media dei valori ottenuti e approssima nel modo matematico
recognition(); // attiva la funzione prima scritta
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Ho svolto il lavoro di sperimentazione utilizzando il sensore di luminosità e l’NXT. Una volta ottenuti dei dati ho cercato di trovare il modo migliore per collegare un numero a un’intensità di grigio,
considerando che avevo bisogno di dieci diversi livelli di grigio (9 numeri e la casella vuota).
15
16
Nicolas Lanzetti
Lavoro di maturità
!
2012
screen();
if (a[i][j][0] > 0) // mi serve per memorizzare se un numero è
già dato o no
{
a[i][j][10]=0; // memorizza che il numero era già scritto
num=num+1; // tiene il conteggio dei numeri conosciuti
}
Wait (500);
if (j < 8) // non esiste una nona riga
{
RotateMotor(OUT_B, 50, 111); // movimento verso il basso
}
}
if(i < 8) // non esiste una nona colonna
{
RotateMotor(OUT_C, 40, -53); // movimento verso destra del
robot
Wait (1000);
}
if (i < 8)
{
RotateMotor(OUT_B, 50, -111*8); // tornare verso l’alto
Wait(500);
RotateMotor(OUT_B, 50, 2);
}
}
void recognition () // confronta il valore del sensore con dei valori sperimentali da me ottenuti provando
{
if (b==c-2) // caso per il numero uno
{
a[i][j][0]=1;
num=num+1;
}
else if (b==c-4 || b==c-3) // caso per il numero due
{
a[i][j][0]=2;
num=num+1;
}
else if (b==c-5 || b==c-6 || b==c-7) // caso per il numero tre
{
a[i][j][0]=3;
num=num+1;
}
[…]
}
!
iii. La risoluzione del Sudoku
La risoluzione dell’enigma avviene tramite un programma integralmente scritto da
me. Come anticipato, il software di risoluzione è stato prima creato in C++ e successivamente integrato nel programma. Esso è in grado di risolvere in una frazione
17
Nicolas Lanzetti
!
Lavoro di maturità
2012
di millesimo di secondo16 Sudoku di livello facile e medio17, ma non difficile. Questo
perché utilizza un sistema ad esclusione abbastanza elementare, che però non basta
a risolvere un Sudoku difficile.
Il Sudoku qui a fianco mostra facilmente perché il
X
X
mio programma non è in grado di risolvere enigmi
2
8 3 Y 7
difficili. Siamo tutti d’accordo che l’1 andrà in una
4 5 6 1 delle due posizioni X, ma malgrado questo dubbio
possiamo affermare con sicurezza che ci sarà un 1
1
nella casella Y. Questo perché nella prima riga del
secondo quadrato 3x3 non sarà possibile piazzare l’1, visto che sarà sicuramente
nella prima riga del primo quadrato 3x3 (tenendo sempre conto del fatto che un
numero non può ripetersi due volte nelle stessa riga). Di conseguenza, l’1 andrà nella posizione Y, l’unico posto in cui è possibile situare l’1 nel secondo quadrato 3x3.
Il computer non è in grado di fare questo ragionamento: lui va ad esclusione, finché
non è sicuro non può eliminare un valore possibile. Di conseguenza prima di piazzare un 1 nella posizione Y, deve prima poterlo piazzare nella prima riga (o avere
escluso tutti gli altri possibili numero per quella cella). Un altro esempio può essere
il seguente: il computer non sa che in ogni riga ci devono essere i numeri da 1 a 9,
se ha tutti i valori di una riga trova l’ultimo unicamente perché tutti le altre possibilità sono escluse e non per la consapevolezza che in ogni riga devono apparire tutti i
numeri da 1 a 9.
Ora, dopo aver presentato l’algoritmo, spiegherò come funziona, proponendo anche alcune righe di codice.
La risoluzione del Sudoku avviene, in termini informatici, grazie ad una matrice. Tutti
i numeri iniziali del Sudoku sono memorizzati in una matrice a tre dimensioni: la riga, la colonna e tutte le possibilità per casella. Quando un dato diventa sicuro (perché già presente nel rompicapo o calcolato) viene salvato nella posizione
a[colonna][riga][0]. Quando un numero invece viene escluso il valore
a[colonna][riga][numero] si eguaglia a 0; una volta che otto numeri inizialmente possibili sono esclusi, il valore rimanente diventa definitivo.
Nel codice del robot la procedura di risoluzione è chiamata Solving e viene richiamata una volta terminato il processo di lettura delle celle. Teoricamente sarebbe
più efficiente svolgere questo procedimento in parallelo alla lettura dei dati, così da
risparmiare qualche secondo di calcolo. Il programma di risoluzione è un chiaro
esempio di quello che viene comunemente chiamato Backtracking Algorithm, dove
gradualmente si eliminano candidati fino ad ottenere la soluzione stessa (esclusione).
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
La velocità di risoluzione dipende ovviamente dal computer utilizzato e, anche se in piccolissima
misura, dal Sudoku scelto. Indicativamente il tempo richiesto è comunque di piccole frazioni di secondo.
17
La risoluzione non è sempre garantita: dipende dal livello scelto dal produttore di Sudoku.!
16
18
Nicolas Lanzetti
!
Lavoro di maturità
2012
L’algoritmo può sembrare abbastanza complicato, ma in realtà si basa su un ragionamento piuttosto semplice. Si passa in rassegna ogni numero diverso da zero
(quindi conosciuto, e nelle celle non conosciute la matrice assume il valore 0) e si
esclude quel numero in tutte le caselle della stessa riga, colonna e dello stesso
quadrato, eguagliando la matrice a zero nella possibilità del numero sicuro (se
quindi il numero è un 1, in tutte le caselle della stessa riga, colonna e dello stesso
quadrato della cella in cui si trova l’1 a[colonna][riga][1]=0, il che significa che la
possibilità 1 è esclusa). Una volta eseguita questa procedura per tutte le 81 celle si
entra in quello che ho chiamato sistema di controllo (pagina successiva).
!
void solving () // algoritmo di risoluzione per sudoku facili e medi, è un
algoritmo che funziona ad esclusione (esclude i numeri non possibili)
{
while(z < 15) // prove che deve fare per trovare tutti i numeri, dopodiché si può fermare
{
for (j=0; j < 9; j++) // for per le colonne
{
for (k=0; k < 9; k++) // for per le righe
{
if (a[j][k][0] > 0) // per ogni numero già conosciuto
esclude il numero nelle caselle in cui non può stare (riga, colonna, quadrato)
{
for (l=0; l < 9; l++) // in questo caso prende le
caselle della stessa riga ma di altre colonne
{
if(a[l][k][0]==0)
{
a[l][k][a[j][k][0]]=0; // riga fissa, altre
colonne
}
}
for (l=0; l < 9; l++) // in questo caso prende le
caselle della stessa riga ma di altre colonne
{
if(a[j][l][0]==0)
{
a[j][l][a[j][k][0]]=0; // colonna fissa, altre righe
}
}
if (j%3==0) // quadrato 3x3, considera il resto
delle divisioni delle coordinate per trovare la posizione
{
if(k%3==0) // caso in cui si trova in basso a
sinistra
{
a[j+1][k+1][a[j][k][0]]=0;
a[j+1][k+2][a[j][k][0]]=0;
a[j+2][k+1][a[j][k][0]]=0;
a[j+2][k+2][a[j][k][0]]=0;
}
else if(k%3==1) // caso in cui si trova al centro a sinistra
{
a[j+1][k+1][a[j][k][0]]=0;
19
Nicolas Lanzetti
Lavoro di maturità
!
2012
a[j+1][k-1][a[j][k][0]]=0;
a[j+2][k+1][a[j][k][0]]=0;
a[j+2][k-1][a[j][k][0]]=0;
}
else if(k%3==2) // caso in cui si trova in alto
a sinistra
{
a[j+1][k-2][a[j][k][0]]=0;
a[j+1][k-1][a[j][k][0]]=0;
a[j+2][k-2][a[j][k][0]]=0;
a[j+2][k-1][a[j][k][0]]=0;
}
}
if (j%3==1)
{
if(k%3==0) // caso in cui si trova in basso al
centro
{
a[j+1][k+1][a[j][k][0]]=0;
a[j+1][k+2][a[j][k][0]]=0;
a[j-1][k+1][a[j][k][0]]=0;
a[j-1][k+2][a[j][k][0]]=0;
}
else if(k%3==1) // caso in cui si trova al centro al centro
{
a[j+1][k+1][a[j][k][0]]=0;
a[j+1][k-1][a[j][k][0]]=0;
a[j-1][k+1][a[j][k][0]]=0;
a[j-1][k-1][a[j][k][0]]=0;
}
else if(k%3==2) // caso in cui si trova in alto
al centro
{
a[j+1][k-2][a[j][k][0]]=0;
a[j+1][k-1][a[j][k][0]]=0;
a[j-1][k-2][a[j][k][0]]=0;
a[j-1][k-1][a[j][k][0]]=0;
}
}
if (j%3==2)
{
if(k%3==0) // caso in cui si trova in basso a
destra
{
a[j-1][k+1][a[j][k][0]]=0;
a[j-1][k+2][a[j][k][0]]=0;
a[j-2][k+1][a[j][k][0]]=0;
a[j-2][k+2][a[j][k][0]]=0;
}
else if(k%3==1) // caso in cui si trova al centro a destra
{
a[j-1][k+1][a[j][k][0]]=0;
a[j-1][k-1][a[j][k][0]]=0;
a[j-2][k+1][a[j][k][0]]=0;
a[j-2][k-1][a[j][k][0]]=0;
}
else if(k%3==2) // caso in cui si trova in alto
a destra
{
a[j-1][k-2][a[j][k][0]]=0;
a[j-1][k-1][a[j][k][0]]=0;
20
Nicolas Lanzetti
Lavoro di maturità
!
2012
a[j-2][k-2][a[j][k][0]]=0;
a[j-2][k-1][a[j][k][0]]=0;
}
}
}
}
}
Il sistema di controllo di cui parlavo prima si traduce per l’appunto nelle righe di
codice che si trovano appena qui sotto. Esso permette di verificare se in una cella
tutte le possibilità meno una sono state escluse, così da poter rendere sicuro un valore.
Per ogni numero non ancora sicuro si calcola la somma delle possibilità rimaste,
memorizzando (variabile r1) l’ultimo numero sommato: se esso è uguale alla somma
prima calcolata significa che tutte le altre possibilità sono state escluse e che il numero è sicuro. Esso può quindi essere salvato nello spazio “zero” della matrice
(a[colonna][riga][0]).
for (i=0; i < 9; i++) // sistema di controllo: se tutti valori
sono esclusi il rimanente diventa definitivo; passa in rassegna tutti i valori
{
for (j=0; j < 9; j++)
{
if(a[i][j][0]==0) // il valore non è ancora sicuro
{
somma=0; // inizializzazione sistema di controllo
r1=0;
for (r=1; r < 10; r++)
{
if (a[i][j][r]!=0) // somma solo i valori diversi da zero (se è uguale a zero il valore è stato escluso)
{
somma=somma+r;
r1=r;
}
}
if (somma==r1) // se la somma dei valori diversi da
zero è uguale al valore per cui è diverso da zero, questo valore diventa
sicuro
{
a[i][j][0]=r1;
num=num+1; // aggiunge uno al numero di valori
21
Nicolas Lanzetti
!
Lavoro di maturità
2012
conosciuti (non funziona)
}
}
}
}
screen(); // aggiorna il lo schermo
z++; // variabile del ciclo while
}
A questo punto tutto il procedimento, incluso quanto descritto precedentemente,
viene rieseguito, utilizzando anche i valori appena scoperti. Facendo delle prove ho
scoperto che 15 cicli sono più che sufficienti per la risoluzione di un Sudoku. Nel
programma originario la computazione si fermava quando il numero di celle sicure è
uguale a 81, ma nel caso di questo software ho avuto dei problemi con le variabili
(una variabile chiamata num aggiungeva 1 ogni volta che trovava un numero) e
quindi il sistema non funzionava correttamente: il conflitto è probabilmente dovuto
al fatto che la variabile rientra in più parti del codice (sia quando i numeri sono dati
all’inizio sia quando sono calcolati).
La procedura Sudoku si conclude con un piccolo algoritmo che calcola se il Sudoku
è impossibile. Se la somma dei numeri è infatti diversa da 405 (9× !!!! i) ci deve essere un qualche errore, che viene segnalato (compare una scritta sullo schermo).
for (i=0; i < 9; i++) // calcola la somma totale dei numeri per verificare l'impossibilità
{
for (j=0; j < 9; j++)
{
sommatot=sommatot+a[i][j][0];
}
}
if (sommatot != 405)// caso in cui è impossibile
{
ClearScreen();
string q="Il Tuo Sudoku è Impossibile."; // variabile string,
contiene un testo, che è quello che mostro sullo schermo, appena finito di
eseguire la procedura solving, se il sudoku risulta impossibile
TextOut(10, 24, q); // API per mostrare un testo sullo schermo
}
}
!
iv. La scrittura dei numeri
La scrittura dei numeri all’interno di un Sudoku non richiede grandi ragionamenti o
calcoli, bensì una lunga fase sperimentale: bisogna provare più volte fino a trovare
l’insieme di valori corretti che permettono una scrittura elementare del numero.
Esso è infatti scritto seguendo precisi schemi geometrici per evitare curve o movimenti obliqui, che richiederebbero il movimento contemporaneo di due motori. Ho
dunque dovuto, prima di tutto, studiare quale fosse il modo migliore per scrivere i
nove numeri, per poi tradurre tutto ciò in movimento di motori.
22
Nicolas Lanzetti
!
Lavoro di maturità
2012
Per quest’ultimo passaggio della risoluzione è necessario avere una buona precisione meccanica, poiché i tre motori devono lavorare quasi contemporaneamente e
mantenere comunque un certo grado di precisione.
Ora presenterò una parte del codice che, come per l’identificazione dei numeri,
passa in rassegna ogni singola casella: se quest’ultima conteneva un numero già
stampato non lo riscrive, altrimenti lo scrive. Infatti, quando il robot prende i dati dal
Sudoku, memorizza nella posizione a[colonna][riga][10] se il numero era già presente nel rompicapo (nel caso sia conosciuto eguaglia a zero). Se invece la cella è ancora vuota il robot procede alla scrittura del numero, utilizzando una procedura di
nome Writing1 (di cui parlerò più avanti). Il movimento del braccio meccanico è regolato da due cicli for e da alcuni if, che evitano il movimento quando si è all’ultima
riga (non si può più scendere) e all’ultima colonna (non si può andare più in là).
for (g=0; g < 9; g++) // devo introdurre nuove variabili, altrimenti
non funziona (probabilmente perché è inclusa una procedura prima dichiarata
{
for (h=0; h < 9; h++)
{
if (a[g][h][10]==0) // caso in cui il numero era già‡ scritto
{
if (h < 8) // non esiste una nona riga
{
RotateMotor(OUT_B, 50, 111); // movimento verso il basso
}
}
else // caso in cui i valore è da scrivere
{
writing(); // procedura contenente tutte le informazioni
su come scrivere un numero
if (h < 8) // non esiste una nona riga
{
RotateMotor(OUT_B, 50, 111); // movimento verso il basso
}
}
}
if (g < 8) // non esiste una nona colonna
{
RotateMotor(OUT_C, 50, -53); // movimento verso destra del
robot
Wait (1000);
}
if (g < 8)
{
RotateMotor(OUT_B, 50, -111*8); // lo fa tornare in cima
Wait(500);
RotateMotor(OUT_B, 50, 2);
}
Nelle righe di codice appena proposte si richiama una procedura di nome Writing1
quando si deve scrivere un numero. Questa procedura si occupa infatti della scrittura di tutti i numeri da 1 a 9, mediante l’utilizzo dei tre motori. La struttura del codice
è piuttosto semplice: si basa su degli if (per vedere che numero scrivere); c’è un alli23
Nicolas Lanzetti
!
Lavoro di maturità
2012
neamento per scrivere il numero, poi si abbassa la penna, si muovono i motori (scrittura), si rialza la penna, ci si riallinea e si va alla casella successiva pronti per scrivere
un nuovo numero (motivo per cui nell’ultima casella il robot, anziché cambiare casella lungo l’asse Y, si muove lungo l’asse X, cambiando dunque colonna). Il seguente codice mostra come si scrive il numero uno (il più semplice), il due e il tre.
void writing () // procedura che racchiude tutte le informazioni necessarie
per la scrittura dei numeri.
{
if (a[g][h][0]==1) // movimenti che deve fare per scrivere il numero 1
{
RotateMotor(OUT_A, 30, 30); // abbassa la penna
Wait(500);
RotateMotor(OUT_B, 50, 66);
Wait(500);
RotateMotor(OUT_A, 30, -30); // alza la penna
Wait(500);
if (i < 8) // nel caso sia l'ultima riga non può abbassarsi ulteriormente
{
RotateMotor(OUT_B, 50, 45);
}
else
{
RotateMotor(OUT_B, 50, -66);
}
Wait(500);
}
else if (a[g][h][0]==2) // movimenti che deve fare per scrivere il numero 2
{
RotateMotor (OUT_B, 50, -50); // serve per allineare lo spostamento orizzontale
Wait(500);
RotateMotor(OUT_C, 30, 15);
Wait(500);
RotateMotor (OUT_B, 50, 50);
Wait(500);
RotateMotor(OUT_A, 30, 30); // abbassa la penna, inizio scrittura
Wait(500);
RotateMotor(OUT_C, 30, -20);
Wait(500);
RotateMotor(OUT_B, 50, 30);
Wait(500);
RotateMotor(OUT_C, 30, 20);
Wait(500);
RotateMotor(OUT_B, 50, 30);
Wait(500);
RotateMotor(OUT_C, 30, -20);
Wait(500);
RotateMotor(OUT_A, 30, -30); // alza la penna
Wait(500);
RotateMotor(OUT_C, 30, 5);
Wait(500);
if (i < 8) // nel caso sia l'ultima riga non può abbassarsi ulteriormente
{
RotateMotor(OUT_B, 50, 51);
}
else
24
Nicolas Lanzetti
!
Lavoro di maturità
2012
{
RotateMotor(OUT_B, 50, -60);
}
Wait(500);
}
else if (a[g][h][0]==3) // movimenti che deve fare per scrivere il numero 3
{
RotateMotor (OUT_B, 50, -50);
Wait(500);
RotateMotor(OUT_C, 30, 15);
Wait(500);
RotateMotor (OUT_B, 50, 50);
Wait(500);
RotateMotor(OUT_A, 30, 30); // abbassa la penna
Wait(500);
RotateMotor(OUT_C, 30, -20);
Wait(500);
RotateMotor(OUT_B, 50, 30);
Wait(500);
RotateMotor(OUT_C, 30, 20);
Wait(500);
RotateMotor(OUT_C, 30, -20);
Wait(500);
RotateMotor(OUT_B, 50, 30);
Wait(500);
RotateMotor(OUT_C, 30, 20);
Wait(500);
RotateMotor(OUT_A, 30, -30); // alza la penna
Wait(500);
RotateMotor(OUT_C, 30, -15);
Wait(500);
if (i < 8) // nel caso sia l'ultima riga non può abbassarsi ulteriormente
{
RotateMotor(OUT_B, 50, 51);
}
else
{
RotateMotor(OUT_B, 50, -60);
}
Wait(500);
}
[…]
}
Un problema che si è presentato in questa fase del programma è quello della precisione: quando i numeri sono “lontani” dal robot, esso tende a non muovere il braccio a causa dell’attrito con il foglio (si muove meno di quanto dovrebbe, rimanendo
leggermente storto). Questo problema è principalmente dovuto al Lego, i cui pezzi
non permettono la realizzazione di una struttura perfettamente rigida: utilizzando
una struttura sviluppata su misura in ferro, il problema si presenterebbe in misura
molto minore. Ho cercato tuttavia di risolverlo: accentuando il movimento orizzontale la scrittura dei numeri risulta buona che nelle celle “lontane” dal robot. Ho dunque creato una seconda procedura, chiamata Writing2, che sostituisce la Writing1
quando deve essere scritto un numero nelle quattro caselle più lontane dal corpo
25
Nicolas Lanzetti
!
Lavoro di maturità
2012
centrale della macchina. La struttura della procedura è uguale: cambiano solo i dati
di rotazione del motore C (movimento orizzontale).
v.
Il task errore
Quando il robot “lavora” è sempre possibile incappare in errori di lettura, quindi ho
scelto di integrare nel programma un task18 parallelo al programma principale di risoluzione, in grado di ovviare a problemi di input. Infatti premendo il sensore di tatto il robot rianalizza tutti le caselle della colonna in questione, così da evitare errori
dovuti, per esempio, ad un posizionamento sbagliato.
Ovviamente il robot deve essere sempre pronto a ricevere un segnale dal sensore
di tatto, quindi questo compito deve essere eseguito parallelamente alla risoluzione
del Sudoku. In termini informatici ciò si traduce in due task (task sudoku e task error), sempre attivi all’interno del task main.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Con il termine task si indica un compito che il computer svolge. Esso deve essere indicato nel codice ed è fondamentale per l’utilizzo di funzioni in parallelo, come una macchina che va avanti ma
che allo stesso tempo controlla che non sbatte.
18
26
Nicolas Lanzetti
!
Lavoro di maturità
2012
5. Il sito web
Oltre ad aver costruito il robot e ad aver redatto la parte teorica, ho deciso di sviluppare anche un sito web del mio lavoro di maturità, raggiungibile all’indirizzo
www.solversudoku.tk. Il sito web è stato costruito grazie alla piattaforma Wix19 e
registrato con un dominio tk20.
All’interno del portale online ho pubblicato una breve descrizione del mio robot e
del mio lavoro di maturità, che può inoltre essere scaricato gratuitamente. Questo
permette un rapido accesso al mio LAM e al codice del robot.
!
!
6. Il concorso
Ho deciso di iscrivere il mio lavoro di maturità a un concorso di robotica. Ritengo
infatti importante e arricchente potersi confrontare con altre persone e ricevere
consigli e giudizi da persone che, come me, stanno muovendo i primi passi nella
robotica e da persone che invece conoscono questi argomenti già da molto tempo.
Questi motivi mi hanno spinto a iscrivermi a bugnplay.ch21, un concorso per ragazzi
fino a 20 anni, al quale è possibile presentare il proprio progetto (Lavoro di maturità
incluso).
!
!
!
!
!
!
!
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Wix è un costruttore di siti online che ti permette di creare gratuitamente siti in HTML5 o Flash
senza conoscenze di programmazione.
20
Il dominio tk è un dominio offerto da Tokelau (Nuova Zelanda) che ha particolarità di essere completamente gratuito.!
21
Bugnplay.ch è un concorso mediale e di robot promosso dalla Migros. Tutte le informazioni sono
reperibili sul sito web www.bugnplay.ch.
19
27
Nicolas Lanzetti
!
Lavoro di maturità
2012
7. Conclusioni
La costruzione del mio Sudoku Solver ha presentato diversi ostacoli, ma sono
estremamente soddisfatto del lavoro svolto, che va molto oltre quanto scritto in
queste pagine e quanto si vede osservando il robot. Ho dovuto pensare molto a
come realizzare la meccanica e a come strutturare la parte informatica: le difficoltà e
i dubbi non sono mancati, ma sono sempre riuscito a risolverli in modo soddisfacente. Ovviamente si può fare meglio: con il famoso “senno di poi” avrei fatto diversamente alcune cose, ma per accorgermene ho dovuto provare, testare, rimanere
ore davanti al computer o alla scatole di Lego.
La scelta del lavoro di maturità è stata perfetta: ho imparato molto e ho avuto a che
fare con argomenti che mi hanno sempre affascinato. Mai mi sono annoiato, mai ho
detto “Che noia questo LAM”. E questa, per me, è indubbiamente la cosa più importante.
8. Ringraziamenti
Desidero infine ringraziare tutte le persone che hanno contributo alla realizzazione
del mio Lavoro di Maturità.
In primo luogo ci tengo a ringraziare il professor A. Strupler e il professor R. Duse,
che durante questo anno di lavoro mi hanno assistito e consigliato. Un ringraziamento va anche al professor L. Rovelli, che mi ha più volte aiutato a sviluppare la
teoria matematica dei Sudoku.
Fondamentale è stato anche l’aiuto della mia famiglia e dei miei zii che mi hanno
più volte aiutato e, soprattutto, corretto la parte teorica.
Dico anche grazie i miei compagni del LAM, in particolare Simone Guggiari e Filippo Kusch, con cui ho più volte parlato, discusso e mi sono confrontato, migliorando
continuamente il mio lavoro.
Non da ultimo ringrazio tutte le persone, che, più o meno volontariamente, hanno
contribuito alla realizzazione di questo progetto.
28
Nicolas Lanzetti
!
Lavoro di maturità
2012
9. Fonti
Tutto il testo e il codice sono stati scritti da me. Le informazioni e le API provengono dalle fonti citate, ma il loro utilizzo e la loro applicazione sono integralmente frutto del mio lavoro.
I programmi utilizzati per la realizzazione del Lavoro di maturità sono stati: Bricx CC,
Google Chrome, Pages, Pixelmator, Microsoft Word per Mac e Xcode.
!
a) Bibliografia
Keith Devlin, The Millennium Problems, Basic Books, New York, 2002.
Marcus du Sautoy, L’equazione da un milione di dollari, Rizzoli, Milano, 2010.
Daniele Benedettelli, Programming LEGO NXT robots using NXC, 2007.
NXC, Generated by Doxygen, 2011.
Avi Wigderson (1956), The P vs. NP problem: Efficient Computation, Internet Security, and the Limits to Human Knowledge (21.01.2010).
http://princetonacm.acm.org/downloads/PvsNP.pdf (15.12.2012).!
b) Sitografia
www.wikipedia.org alla voce Sudoku, 24.09.2012.
www.wikipedia.org alla voce Jacques Ozanam, 09.10.2012.
www.wikipedia.org alla voce Leonhard Euler, 09.10.2012.
www.wikipedia.org alla voce Stephen Cook, 09.10.2012.
www.wikipedia.org alla voce P versus NP problem, 15.12.2012
www.wikipedia.org alla voce Lego Mindstorms NXT, 19.10.2012.
www.wikipedia.org alla voce Application Programming Interface, 24.09.2012.
29
Nicolas Lanzetti
!
Lavoro di maturità
2012
www.wikipedia.org alla voce Sudoku Algoritms, 20.10.2012.
images.google.com per le immagini.
http://bricxcc.sourceforge.net/
www.sjf.ch per la redazione (http://it.sjf.ch/datei/ACF54.pdf).
www.wix.com, 10.11.2012.
www.bugnplay.ch, 15.12.2012.
!
!
!
!
!
!
!
!
!
Dichiaro di avere svolto il presente lavoro di maturità autonomamente e
senza mezzi illeciti e che tutte le fonti, i mezzi ausiliari e i siti Internet utilizzati sono stati dichiarati e impiegati onestamente.
Nicolas Lanzetti
Tutti i diritti riservati. ®
30