SlideShare ist ein Scribd-Unternehmen logo
1 von 102
Downloaden Sie, um offline zu lesen
Università degli Studi di Trieste
             Facoltà di Ingegneria



        Tesi di Laurea Specialistica
                     in
          Ingegneria Informatica


     PROGETTAZIONE E
   REALIZZAZIONE DI UNA
    APPLICAZIONE PER IL
       GIOCO DEGLI
       "SCACCHI 3D"

LAUREANDO:              RELATORE:
                        Chiar.mo Prof.
Bruno TAGLIAPIETRA      Maurizio FERMEGLIA


         Anno Accademico 2008/09
                         200
2
Indice
Introduzione                                                  5
  Motivazione                                                 5
Analisi                                                       7
  Descrizione del gioco preso in esame                        7
     Raumschach                                               7
     Pezzi                                                    9
     Scopo del gioco e relativo regolamento                  16
     Notazione delle mosse                                   16
  Requisiti funzionali                                       17
  Requisiti non funzionali                                   18
Progettazione                                                19
  Considerazioni preliminari                                 19
  Architettura del sistema                                   20
     Interfaccia – Motore VS Model – View - Controller       20
  Progettazione del Motore                                   21
     Rappresentazione della scacchiera e generazione mosse   23
     Tecniche di ricerca                                     32
     Valutazione di una posizione                            41
  Progettazione dell’Interfaccia                             50
     Scacchiera Tridimensionale                              50
  Comunicazione Interfaccia-Motore                           56
Realizzazione                                                57
  Tecnologie utilizzate                                      57
  Realizzazione del Motore                                   60
     Header files                                            60
     C Files                                                 62
  Realizzazione dei modelli 3D                               75
  Realizzazione dell’Interfaccia                             77
     Premessa: XNA Application Model                         77
     Librerie di terze parti                                 79
     Gameplay                                                81


                                                              3
Videocamere Virtuali                            81
      Gestione dei modelli                            90
      Logiche di gioco                                92
Conclusioni                                           93
    Raggiungimento degli obiettivi                    93
    Sviluppi futuri                                   93
Bibliografia                                          94
      Letteratura                                     94
      Web                                             94
Ringraziamenti                                        96
Appendice                                             97
    Fast, Minimum Storage Ray/Triangle Intersection   97




4
Introduzione

Motivazione
Il gioco degli scacchi è considerato un problema molto interessante dal
punto di vista della sua risoluzione e gioco da parte di un elaboratore: nel
corso degli anni, fin dagli albori dell’informatica, gli appassionati e i
professionisti di scacchi e di computer hanno perfezionato algoritmi sempre
più sofisticati, fino a riuscire recentemente a far vincere il computer contro i
più grandi maestri.

Vale la pena ricordare che gli scacchi, a differenza ad esempio della dama,
sono ancora un problema aperto: cioè, mentre nella dama ormai si conosce
la mossa “migliore” in ogni situazione, dall’inizio della partita fino alla fine,

Vi sono competizioni mondiali che consistono in un gran numero di partite
fra i cosiddetti “motori” per il gioco degli scacchi, ovvero software molto
perfezionati e specifici il cui unico obiettivo è vincere una partita di scacchi.

Alcuni fra i motori migliori sono stati poi inseriti nei programmi di scacchi
commerciali, il più famoso di tutti è sicuramente “Chessmaster 10”: presenta
innumerevoli funzionalità quali interfaccia grafica user friendly, possibilità
di selezionare le abilità dell’avversario computerizzato (fino ad arrivare ad
un’abilità stimata con punteggio FIDE 2966), un breve corso di scacchi
integrato...

Sarebbe stato quindi presuntuoso e difficilmente fattibile per un singolo
scrivere qualcosa che sia competitivo sia dal punto di vista del motore, cioè
dell’intelligenza del programma, sia dal punto di vista delle funzionalità
offerte all’utente, rimanendo nel campo degli scacchi classici.

Un’alternativa sarebbe stata inventare un gioco del tutto nuovo. Tuttavia
spesso i giochi complessi difficilmente risultano equilibrati, interessanti e
umanamente giocabili alla prima versione.

Per questi motivi si è deciso di “riesumare” e computerizzare una fra le
versioni più interessanti di questo gioco, che sono indubbiamente quelle
che si svolgono su una scacchiera tridimensionale. Le tre principali
alternative erano: Raumschach (in tedesco “scacchi nello spazio”), inventata
nei primi del ‘900, Asimov Chess (versione ideata da Isaac Asimov in uno
dei suoi racconti, ma mai sufficientemente giocata), Star Trek 3D Chess
(versione giocata nell’omonima saga).




                                                                               5
Si sono scelti i Raumschach fra i tre perché essi sono stati indubbiamente i
più giocati e i più sperimentati; inoltre non esiste alcuna implementazione
sotto forma di videogame che sia fedele all’originale di questo gioco, ideato
e giocato in alcuni club tedeschi nel periodo tra la prima e la seconda guerra
mondiale.




6
Analisi

Descrizione del gioco preso in esame
La versione scelta è, fra le versioni tridimensionali esistenti del gioco degli
scacchi, la più documentata e la più giocata in assoluto.
Le versioni tridimensionali del gioco degli scacchi, abbreviabili in 3D chess,
fanno parte delle innumerevoli varianti di questo famosissimo gioco. Le più
antiche risalgono alla fine del 1800.
Una delle più famose versioni tridimensionali degli scacchi è appunto quella
analizzata in questo documento. Essa è detta Raumschach, ovvero “scacchi
spaziali” in lingua tedesca.
Inventata nel 1907 da Ferdinand Maack, è stata giocata prevalentemente in
Germania. Nel 1919 Maack fondò anche un club di Raumschach ad
Amburgo, rimasto attivo fino all’inizio del secondo conflitto mondiale.
Inizialmente giocata in un cubo 8x8x8, la versione del 1907 vide la luce su
una scacchiera cubica con cinque caselle per ognuna delle tre dimensioni.


Raumschach
Come negli scacchi classici l’obiettivo è catturare il re
avversario. Questo può avvenire solo tramite lo
“scacco matto”, ossia una posizione in cui non vi è
mossa in grado di impedire la cattura del re nella
mossa immediatamente successiva.
I contendenti muovono uno dei propri pezzi alla
volta, a turno, alternandosi. I movimenti, descritti più
avanti in dettaglio, sono simili a quelli degli scacchi
classici.
Ognuna delle 125 caselle è identificata da tre
coordinate: “livello” di appartenenza, “riga” e
“colonna”, in quest’ordine.
        •     Livelli: identificati dalle lettere Fig. 1 Schema della scacchiera
        maiuscole A,B,C,D,E
        •     Righe: identificate dalle cifre 1,2,3,4,5
        •     Colonne: identificate dalle lettere minuscole a,b,c,d,e

Seguendo la disposizione di fig.2 si inizia, per convenzione, la numerazione
delle caselle da quella posta sul livello più basso, sulla colonna più a sinistra
e sulla riga più vicina al giocatore con i pezzi di colore bianco. Perciò, nella
casella Aa 1 vi sarà all’inizio partita una torre “bianca”, e in Ba1 nel
medesimo istante un alfiere “bianco”, con il re posto nel piano più basso.
Sempre da fig.2 è facile ed importante notare che la disposizione dei pezzi


                                                                               7
neri risulta speculare a quella dei pezzi bianchi, quindi con il re posto sul
                                                                     posto
livello più alto. Quindi in Ee5 vi sarà una torre “nera” e in Ed5 un unicorno
“nero”.




Fig. 2 - La disposizione iniziale della scacchiera, dal piano più alto (E) al piano più basso (A)




8
Pezzi
I pezzi sono, come negli scacchi classici, Re, Regina, Torri, Alfieri, Cavalli e
                             scacchi
Pedoni, con l’aggiunta degli Unicorni. Ogni giocatore ha a disposizione 10
pedoni, 2 torri, 2 cavalli, 2 alfieri, 2 unicorni, 1 regina e naturalmente 1 re.
Come negli scacchi classici su ciascuna casella può stazionare un solo pezzo
                                                      può
e l’unico in grado di “saltare”, cioè di non subire l’ostruzione degli altri pezzi
presenti sulla scacchiera, è il cavallo. Qualsiasi pezzo di un certo colore può
occupare una casella contenente un pezzo avversario, catturando in questoi
modo quest’ultimo, il quale verrà rimosso dalla scacchiera.

Pedone: come negli scacchi classici il suoi movimenti cambiano a seconda
che esso catturi o meno un pezzo. Senza cattura esso deve muovere di un
passo di torre verso il campo avversario; se “bianco”, quindi, può muovere
verso l’alto o verso avanti rispetto al giocatore che lo controlla. In caso di
cattura a questo movimento dovrà essere aggiunto un cambio di colonna
verso destra o verso sinistra, risultante in un passo d’alfiere sullo ste stesso
piano o sul piano immediatamente successivo nel viaggio verso il campo
avversario. A differenza degli scacchi classici non c’è differenza di possibilità
di movimento fra la prima mossa e le successive. Non c’è mossa en-passant.

Lettera identificativa: P




                                                                                9
Torre:

può muovere in linea retta di quante caselle si vuole in un solo turno. Può
muoversi in sei versi; è utile immaginare che essa per muoversi esca da una
delle sei facce del cubo di partenza.


Es: una torre posta al centro di una scacchiera vuota, quindi in Cc3, si può
  :                                             vuota,
muovere in

                o      Cc1, Cc2,Cc4, Cc5 (Varia la riga)
                o      Ca3, Cb3, Cd3, Ce3 (Varia la colonna)
                o      Ac3, Bc3, Dc3, Ec3 (Varia il livello)
         Lettera identificativa: R (dall’inglese “rook”)




10
Alfiere: anch’esso muove in linea retta per quante caselle si vuole in un solo
                                                   caselle
turno. A differenza della torre però esso esce dagli spigoli del cubo, perciò
può muoversi in otto versi.


Es: Un alfiere posizionato nella casella Dc4 in una scacchiera vuota può
  :
muoversi in

   •   Da2, Db3, Dd5, De2, Dd3, Db5
              (nello ste
                     stesso livello, come negli scacchi classici)
   •   Eb4, Ed4, Cb4, cd4, Ba4, Be4
              (nel piano posto frontalmente al giocatore)
   •   Ec3, Ec5, Cc3, Cc5, Bc2, Ac1
              (nel piano posto lateralmente al giocatore)
       Lettera identificativa: B (dall’ingelse “bishop”)




                                                                            11
Unicorno: anch’esso in linea retta per quante caselle si vuole in un solo
            nch’esso
turno, ma uscendo dai vertici del cubo. Perciò si muove in quattro versi.
Es: un unicorno posizionato al centro di una scacchiera vuota (Cc3) può
  :
essere posto in:


Dd4, Ee5, Bb2, Aa1, Db2, Ea1, Bd4, Ae5, Ee1, Dd2, Bb4, Aa5, Ea5, Db4, Bd2,
                            ,
Ae1
Lettera identificativa: U




12
Regina: essa può muovere sia come una torre, sia come un alfiere, sia come
un unicorno.
Lettera identificativa: Q (dall’inglese “queen”)




                                                                         13
Re: esso si sposta come la regina però di un solo passo per turno. Ovvero, la
                           regina
casella di arrivo deve essere adiacente alla casella di partenza cioè avere in
comune con essa una faccia, uno spigolo o alla peggio un solo vertice. In
altre parole un re può muovere in ciascuna delle otto caselle più vicine alla
                                                         caselle
casella di partenza. A differenza degli scacchi classici non vi sono manovre
di arrocco.


Lettera identificativa: K (dall’inglese “king”)




14
Cavallo: E’ l’unico pezzo in grado di saltare oltre gli altri pezzi. Si deve
muovere di un solo passo di torre più un solo passo di alfiere. In altre
                  olo
parole, date le coordinate della casella di partenza, una di esse deve restare
invariata, una deve variare di 1 e la terza deve variare di 2.


Es: in questi esempi indichiamo il variare delle coordinate racchiudendo tra
  :                                                coordinate
parentesi l’entità della variazione di livello, riga e colonna, in quest’ordine.
Perciò ad esempio la tripla (0,1,2) indicherà un insieme di caselle poste
contemporaneamente nello stesso livello di quella di partenza, su righe
distanti 1 e colonne distanti 2.
        i


Un cavallo posto al centro di una scacchiera, nella casella Cc3, può essere
posto in ciascuna delle 24 caselle sottostanti, purché libere da pezzi dello
stesso colore:

        Cb1, Cb5, Cd1, Cd5    (0,1,2)   ---------   Ca2, Ca4, Ce2, Ce4   (0,2,1)
        Bc1, Bc5, Dc1, Dc5    (1,0,2)   ---------   Ba3, Be3, Da3, De3   (1,2,0)
        Ab3, Eb3, Ad3, Ed3    (2,1,0)   ---------   Ac2, Ec4, Ac4, Ac2   (2,0,1)
       Lettera identificativa: N (dall’inglese “knight”)




                                                                                   15
Scopo del gioco e relativo regolamento
Come negli scacchi classici, lo scopo del gioco consiste nel dare "scacco
matto" (dal persiano Shah Màt = il re è morto) al re avversario; si ha "scacco
matto" quando il Re, trovandosi sotto la minaccia diretta dei pezzi avversari,
non ha la possibilità di sottrarsi ad essa (cioè sarebbe sicuramente catturato
alla mossa successiva, se non si trattasse del Re).
Lo "scacco" invece è l'attacco (evitabile) che un pezzo avversario porta al Re.
L'avversario non può eseguire alcuna mossa che metta o lasci il proprio re
sotto "scacco". La partita può terminare anche per abbandono da parte di
un contendente, ovviamente con la vittoria dell'altro.

Il gioco termina obbligatoriamente in parità (patta) nei seguenti casi:

       1.     se restano sulla scacchiera soltanto i due re;
       2.     se il giocatore che ha il tratto non può muovere alcun pezzo,
       ma il suo re non è sotto scacco (stallo).
       3.     se per cinquanta mosse consecutive (cinquanta mosse per
       ciascun giocatore) non viene catturato alcun pezzo e non viene
       mosso alcun pedone.

Notazione delle mosse

Durante il corso della storia sono state ideate innumerevoli notazioni per gli
scacchi classici. Senza dubbio la più utilizzata, per le sue caratteristiche di
intuitività e brevità, è la cosiddetta “notazione algebrica”, descritta nel
regolamento internazionale degli scacchi. Risulterebbe però problematico, e
con tutta probabilità controproducente: infatti in essa si preferisce, per
identificare una mossa, annotare la lettera iniziale del nome del pezzo che
la esegue seguita dalle coordinate della casella di arrivo.

Es: Cf3 (cavallo mosso in f3, non si sa da quale punto di partenza).

E’ evidente che tale notazione necessita di complesse regole per evitare
equivoci nel caso in cui, ad esempio, i cavalli che possono muovere in f3
siano più di uno.

Per questo motivo, la notazione adottata nel nostro caso consisterà in:

casella di partenza – casella di arrivo – iniziale di eventuale pezzo “di
promozione”

es:    Aa1Ca1        (il pezzo presente in Aa1 va in Ca1),
       Ec4Ec5Q      (il pezzo, pedone, presente in Ec4 va Ec5 e viene
       promosso in regina);




16
Requisiti funzionali

Di seguito sono riportati i requisiti funzionali raggruppati in
macrofunzionalità. Ciascuna macrofunzionalità è raggiunta nel momento in
cui vengono soddisfatti tutti i requisiti in esso contenuti.

Requisiti giocabilità 2 giocatori su stessa postazione

   •   Il software deve presentare una scacchiera cubica 5x5x5, conforme al
       regolamento indicato precedentemente. Detta scacchiera deve essere
       inizialmente configurata come da Fig.2 e contenere tutti e soli i pezzi
       indicati in Fig.2.

   •   Si vuole inoltre che i movimenti consentiti sui pezzi siano conformi
       al regolamento precedentemente indicato.

   •   Essendo la visualizzazione di una scacchiera 3D ostica all’essere
       umano, si vuole che vi siano più modalità di visualizzazione della
       stessa (lontano-vicino).

   •   Per selezionare il pezzo da muovere e conseguentemente la casella in
       cui muoverlo si vuole poter usare il mouse: un primo click sul pezzo
       lo deve evidenziare, un secondo click fuori dal pezzo deve:

          o Spostare il pezzo se il click è stato effettuato su una casella
            dove il pezzo stesso possa muovere.

          o Selezionare un pezzo diverso se il click è stato effettuato su un
            altro pezzo selezionabile.

          o Deselezionare il pezzo selezionato altrimenti.




                                                                            17
Requisiti giocabilità              giocatore       umano         vs    giocatore
computerizzato

     •   Si vuole che vi sia la possibilità di giocare contro un avversario
         computerizzato.

     •   Si vuole che l’abilità di detto avversario sia regolabile. Per la
         precisione:

            o Deve essere possibile regolare il tempo massimo concesso per
              eseguire una mossa.
            o Deve essere possibile fissare la profondità di analisi massima
              del software.


Requisiti giocabilità 2 giocatori umani su postazioni diverse

     •   Si vuole che vi sia possibilità di giocare via internet con un avversario
         umano.

Requisiti non funzionali

     •   Portabilità: si vuole che il gioco sia cross platform il più possibile.




18
Progettazione

Considerazioni preliminari
Dallo studio dei requisiti emerge come prima peculiarità il fatto che si tratti
di arrivare alla realizzazione di un videogame. Già questa semplice
particolarità presenta non poche implicazioni progettuali:

Restringendo il campo e riassumendo, dal punto di vista informatico si
tratta di un gioco da tavolo (board videogame) dotato di “intelligenza
artificiale” (AI), che deve quindi essere in grado di tener testa ad un
giocatore umano.

Si delina quindi una prima suddivisione in “motore”, ossia la parte pensante
del gioco, e “interfaccia utente”, cioè tutto ciò che è percepito
sensorialmente dal giocatore umano.
Dovendo essere intelligente, il motore deve avere come caratteristiche
peculiari rapidità di calcolo, efficienza, ed un certo grado di “regolabilità
della difficoltà”.
L’interfaccia, per contro, dovrà soddisfare i requisiti richiesti; inoltre, non
avendo particolari requisiti di efficienza, sarà facile progettarla in modo che
sia il più manutenibile possibile.
Altro punto fondamentale, si tratta di un software che fa uso di grafica 3D.

Sarà quindi necessaria la scelta di tecnologie adatte a coprire le necessità
emerse da queste prime considerazioni iniziali. In particolare si avrà
bisogno di:

   •   Un game engine con supporto 3D (OpenGL, Direct3D o entrambi).
       E’ un software che serve a sviluppare video game. Le funzionalità
       principali fornite da un game engine comprendono un renderer per
       grafica 2D e/o 3D, animazioni, networking, gestione della memoria,
       threading.
       In più quello da noi scelto dovrà facilitare la portabilità, come da
       requisito.

   •   Un software di 3D modeling per la realizzazione dei modelli (i pezzi,
       la scacchiera).




                                                                            19
Architettura del sistema

Nella primissima fase della definizione architetturale è opportuno
mantenersi ad alti livelli di astrazione, scendendo nei dettagli il meno
possibile. Via via che l’architettura prende forma, la definizione delle
singole parti sarà sempre più dettagliata.

Interfaccia – Motore VS Model – View - Controller
In accordo alle considerazioni preliminari, in prima approssimazione viene
molto naturale suddividere il progetto in due macroparti, come segue:

     •    Interfaccia Utente
          Si occupa di raccogliere input dall’utente (le mosse che egli intende
                                            dall’utente
          fare) e di visualizzare a video la scacchiera.

     •    Motore

          In esso sono implementate le regole del gioco, lo stato della partita e
                                                  gioco,
          la logica di gioco computerizzato.

In questo modo, ad ogni input dell’utente l’Interfaccia Utent dovrà:
                                                        Utente

     1.   Compiere un’operazione di traduzione dell’input.
     2.   Passare al Motore la suddetta traduzione.
     3.   Attendere risposta del Motore.
     4.   Aggiornare la visualizzazione basandosi sullo stato del Motore.

Ad ogni input ricevuto, il Motore dovrà:

     1. Porre a confronto l’input ricevuto con le regole.
     2. Se l’input ricevuto corrisponde ad una mossa permessa, modificare lo
        stato e generare un messaggio di conferma.
        Altrimenti, generare un messaggio di errore.
     3. Rispondere con il messaggio generato all’Intefaccia Utente




20                          Fig. 3 Model - View - Controller
L’architettura così descritta richiama il pattern archietturale Model View
Controller. Il comportamento del sistema viene normalmente così illustrato:

   1. L’utente interagisce in qualche modo con l’interfaccia (View)
   2. Il Controller reagisce all’input, ad esempio tramite una callback.
   3. Il Controller propaga sul Modello il risultato dell’azione dell’utente;
      solitamente modifica lo stato del Modello.
   4. Una View utilizza il modello indirettamente per generare
      un’interfaccia utente appropriata, o per modificare quella già
      esistente. Il Controller e il Modello non “sanno” che la View esiste.

Nel nostro caso Model e Controller farebbero entrambi parte del Motore.

Questa è infatti l’architettura più conveniente; tutto lo stato è contenuto nel
Motore, che viene interrogato dall’Interfaccia ogniqualvolta ve ne sia
bisogno.


Progettazione del Motore

Per essere in grado di assolvere alla sua funzione in base ai requisiti
descritti, il Motore di 3D Chess deve innanzitutto possedere un sistema di
strutture dati atte a descrivere lo stato della partita in corso.
Lo stato sarà formato da:

   •   Stato della scacchiera, ovvero stato di ciascuna delle 125 caselle.
       Ciascuna di esse può essere vuota oppure contenere uno dei 7 tipi di
       pezzi disponibili, che possono essere a loro volta “bianchi” o “neri.
       Perciò ogni casella ha 7 ∗ 2 + 1 = 15 stati possibili.

   •   Numero di mosse consecutive dall’ultima cattura di pezzo o mossa di
       pedone (essenziale per soddisfare la regola di partita patta in caso di
       50 mosse consecutive senza catture o mosse di pedoni).

   •   Giocatore che ha il tratto (cioè: tocca muovere al bianco o al nero?)

   •   Inoltre, volendo dare la possibilità di “takeback”, ovvero di poter
       annullare un certo numero di mosse, sarebbe opportuno mantenere
       uno stack delle mosse fatte fino a quel momento. In alternativa, per
       semplificare il calcolo, si potrebbero memorizzare direttamente le
       posizioni della scacchiera.




                                                                               21
A questo punto, basandosi sullo stato così descritto, il Motore deve essere in
grado di:

     •   Esprimere una valutazione quantitativa sulla posizione

     •   Per un certo numero di “livelli”, si esso n:

            o Generare tutte le mosse possibili
            o Valutare tutte le posizioni
            o Per ciascuna posizione, generare tutte le mosse possibili

            o Fermarsi quando ha raggiunto il tempo limite

In questo modo si sarà in grado di fissare il “livello” di difficoltà sia
basandosi sulla massima profondità di esplorazione dell’albero delle mosse,
sia fissando un tempo limite massimo oltre il quale il Motore dovrà
selezionare la mossa ritenuta fino a quel momento la migliore.

Naturalmente, tutto questo deve essere fatto nel modo più efficiente
possibile, in quanto questo è il requisito fondamentale che deve avere il
nostro Motore.

Da queste considerazioni iniziali si può facilmente concludere che tre sono i
problemi fondamentali da risolvere:

     •   Rappresentazione della scacchiera:
         quali strutture dati utilizzare per rappresentare una posizione?

     •   Tecniche di ricerca:
         come identificare le mosse possibili e selezionare quelle che
         sembrano essere più “promettenti” in modo che vengano esplorate
         per prime?

     •   Valutazione di una posizione:
         come valutare quantitativamente una posizione?

Descriviamo ora i punti presi in considerazione nella progettazione della
soluzione a questi tre principali problemi.




22
Rappresentazione della scacchiera e generazione mosse

Nei motori per il gioco degli scacchi classici vengono utilizzati da molti anni
delle strutture dati ormai considerate standard per la rappresentazione di
una scacchiera.
E’ nostro intento adottare la più consona ai nostri scopi, fornendo dapprima
una descrizione delle possibili alternative.


Alternative progettuali

   1. Lista di pezzi
      Era utilizzata dai primi programmi di scacchi, i quali avevano a
      disposizione pochissima memoria. Perciò anziché riferirsi prima alla
      cella e da essa ottenere il pezzo, mantenevano una lista dei pezzi in
      gioco e relative coordinate.
      E’ utilizzata ancora oggi in congiunzione ad altre tecniche nel caso
      serva scoprire rapidamente la posizione di un certo pezzo.

   2. Array based
      Una fra le alternative più intuitive, ovvero creare un array di tante
      caselle quante sono quelle della scacchiera che trattiamo. Ciascun
      elemento dell’array identificherà il contenuto della casella: ad
      esempio assegnando un valore convenzionale a ciascuna delle 15
      possibili combinazioni (vuota, uno dei 7 tipi di pezzi bianchi o uno
      dei 7 tipi di pezzi neri).
      Diventa lento durante la generazione delle mosse in quanto per ogni
      mossa generata bisogna controllare se essa è dentro o fuori dalla
      scacchiera, rallentando in questo modo il processo di generazione.

   3. Codifica Huffman
      Ispirato alla nota codifica di Huffman, le posizioni vengono descritte
      con configurazioni di bit di lunghezza inversamente proporzionale
      alla probabilità che esse compaiono.

       Ecco un esempio che farebbe al caso nostro:
       Casella vuota = 0
       Pedone            = 10c
       Unicorno          = 1100c
       Alfiere           = 1101c
       Cavallo           = 1110c
       Torre             = 1111c
       Regina            = 11110c
       Re                = 11111c

       dove ‘c’ rappresenta il colore (ad esempio 1 per bianco e 0 per nero).


                                                                            23
Servono ancora 1 bit per identificare chi ha il tratto e 7 bit per la
        regola delle 50 mosse.

        Questa strategia risulta molto onerosa dal punto di vista della
        complessità di calcolo, mentre è conveniente per le ridotte
        dimensioni in memoria.

     4. Metodo 0x88:
        Questo metodo funziona negli scacchi classici perché le 64 caselle
        sono in numero una potenza di 2, come pure le due dimensioni 8x8.
        Non può essere utilizzato nel nostro caso (la scacchiera è composta
        da 125 caselle, con tre dimensioni 5x5x5, e non vi sono potenze di 2
        fra questi numeri), ma lo citiamo, senza spiegarlo, per completezza.

     5. Bitboard:
        Anche questo metodo non funziona per noi. Consiste nello sfruttare
        la parallelizzabilità del calcolo in caso di processori a 64 bit quando
        le caselle sono 64, come negli scacchi normali. Anche questo è citato
        solo per completezza

     6. Notazione Forsyth–Edwards (FEN)
        Basata su un sistema ideato dal giornalista scozzese David Forsyth, è
        stata estesa da Steven J. Edwards per utilizzi informatici.
        Un “record” FEN definisce una particolare posizione di gioco; si
        tratta di una stringa di caratteri ASCII tutti sulla stessa riga,
        contenente sei campi, separati da uno spazio.
        I campi sono:

        (NB: la sguente descrizione si riferisce al regolamento degli scacchi
        classici. E’ qui riportata per confronto con la variante FEN elaborata
        per il caso nostro e descritta in seguito)

            a. Posizionamento dei pezzi (dalla prospettiva del bianco)
               Ciascuna delle 8 righe è descritta come segue, ed è separata
               dalle altre utilizzando il carattere “/”.
               Seguendo la notazione algebrica standard (stessa iniziale dei
               pezzi descritta in questo documento nella parte di analisi), i
               pezzi bianchi sono designati usando lettere maiuscole
               (“PNBRQK”) mentre quelli neri con lettere minuscole
               (“pnbrqk”). Le caselle vuote consecutive vengono raggruppate
               e indicate con cifre da 1 a 8.

            b. Colore di chi ha il tratto
               “w” per bianco (white), “b” per nero (black).




24
c. Arrocco
      Se non si può più arroccare si segnerà “-“. Altrimenti il
      vocabolario è il seguente: “K” (il bianco può arroccare dal lato
      di re), “Q” (il bianco può arroccare dal lato di regina), “k” (il
      nero può arroccare dal lato di re), “q” (il nero può arroccare
      dal lato di regina).

   d. En Passant
      Se un pedone può essere mangiato “en passant” si segneranno
      le coordinate della casella sulla quale finirà un eventuale
      pedone avversario che effettuerà la mangiata.
      Altrimenti si segna “-“.

   e. Halfmove clock
      Così si definisce il numero di mezze mosse passate da quando
      è stato mosso un pedone o mangiato un pezzo. Se raggiunge
      100 la partita è pari.

   f. Fullmove number:
      Il numero di mosse complete eseguite fino al momento della
      scrittura della posizione. Comincia da 1 e viene incrementato
      ogniqualvolta muove il nero.

Esempio di record FEN per scacchi classici (appena descritto) nella
posizione iniziale:
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1

Ispirandoci a questa notazione possiamo derivarne con poco sforzo
una rappresentazione appropriata ed intuitiva per il caso preso in
esame da questa tesi.

Notazione Forsyth–Edwards-Raumschach (FENR)
Non contemplando il regolamento del gioco da noi preso in esame la
presa “en passant” né tantomeno alcun tipo di arrocco, i campi che
compongono un record FENR saranno soltanto 4, separati da spazi.
Gli ultimi 3, ovvero “Colore di chi ha il tratto”, “Halfmove clock” e
“Fullmove number”, rimarranno fedeli all’originale.
Il primo, riguardante la descrizione del posizionamento dei pezzi,
sarà annotato come segue.
Come nell’originale la scacchiera verrà rappresentata dal punto di
vista del bianco. I piani saranno scritti dal più alto al più basso. La
rappresentazione delle caselle vuote e le lettere rappresentanti i
pezzi seguiranno le regole dell’originale, con l’aggiunta della “u” e
“U” per gli unicorni rispettivamente neri e bianchi.



                                                                    25
Come nell’originale le righe saranno separate da “/”. I piani, invece,
        saranno separati da “-“.

        Esempio di record FENR per 3D Chess nella posizione iniziale:

        rnknr/ppppp/5/5/5-buqbu/ppppp/5/5/5-5/5/5/5/5-
        5/5/5/PPPPP/BUQBU-5/5/5/PPPPP/RNKNR w 0 0

        (cfr. con fig. 2).

     7. Mailbox Array
        E’ una variante della struttura dati Array Based (descritta al punto 2),
        mantiene i suoi vantaggi ed abbatte la difficoltà di determinare,
        durante la generazione delle mosse, se la mossa generata finisca
        dentro o fuori dalla scacchiera.

        Descriviamo dapprima il metodo utilizzato negli scacchi classici, più
        semplice in quanto riferito ad una scacchiera bidimensionale.

        Per rappresentare la scacchiera si utilizzano 2 array
        monodimensionali di interi.
        Uno formato da 8 ∗ 8 = 64 caselle, denominato “addresser”.
        L’altro formato da 12 ∗ 12 = 144 caselle, denominato “mailbox”.

        Mailbox sarà così composto:

                  -1   -1    -1   -1   -1   -1   -1   -1   -1   -1   -1   -1
                  -1   -1    -1   -1   -1   -1   -1   -1   -1   -1   -1   -1
                  -1   -1     0    1    2    3    4    5    6    7   -1   -1
                  -1   -1     8    9   10   11   12   13   14   15   -1   -1
                  -1   -1    16   17   18   19   20   21   22   23   -1   -1
                  -1   -1    24   25   26   27   28   29   30   31   -1   -1
                  -1   -1    32   33   34   35   36   37   38   39   -1   -1
                  -1   -1    40   41   42   43   44   45   46   47   -1   -1
                  -1   -1    48   49   50   51   52   53   54   55   -1   -1
                  -1   -1    56   57   58   59   60   61   62   63   -1   -1
                  -1   -1    -1   -1   -1   -1   -1   -1   -1   -1   -1   -1
                  -1   -1    -1   -1   -1   -1   -1   -1   -1   -1   -1   -1




26
Addresser sarà invece composto come segue:

     26     27      28      29     30     31     32      33
     38     39      40      41     42     43     44      45
     50     51      52      53     54     55     56      57
     62     63      64      65     66     67     68      69
     74     75      76      77     78     79     80      81
     86     87      88      89     90     91     92      93
     98     99     100     101    102    103    104     105
     110    111    112     113    114    115    116     117

Ovvero addresser conterrà tutti e soli, nell’ordine, gli indici delle
caselle di Mailbox che non contengono −1.

A questi due array si aggiunge la scacchiera Array Based, sia esso
 ℎ          , costituita da un array monodimensionale di interi
composto da 64 caselle. I pezzi vengono rappresentati in esso con
valori convenzionali.
Esempio:

0:   casella vuota
1:   pedone bianco          ,    -1   pedone nero
2:   unicorno bianco        ,    -2   pedone nero
3:   cavallo bianco         ,    -3   cavallo nero
4:   alfiere bianco         ,    -4   alfiere nero
5:   torre bianca           ,    -5   torre nera
6:   regina bianca          ,    -6   regina nera
7:   re bianco              ,    -7   re nero

Ora, per ogni verso in cui ciascun pezzo può muoversi si definisce un
offset. Aggiungendo questo offset all’indice della casella in cui esso si
trova si otterrà la successiva casella nella direzione di movimento di
quell’offset.
Ad esempio, per l’alfiere gli offset saranno 4, uno per ogni direzione,
e saranno −13, −11, 11, 13.
Infatti, un ipotetico alfiere in posizione 115, ovvero la posizione di
partenza dell’alfiere di re bianco, potrebbe muoversi ad esempio
nelle caselle 115 − 13 = 102 e 115 − 11 = 104 (prendendo come
riferimento l’array Addresser). E infatti:

                mailbox[102] contiene 52
                            e
                mailbox[104] contiene 54

Infatti 52 e 54 sono le nuove coordinate dell’alfiere nella scacchiera
Array Based.



                                                                         27
Si vede subito che tentando di far muovere l’alfiere in questione fuori
     dalla scacchiera si ottiene −1.
     Infatti 115 + 11 = 126 e 115 + 13 = 128. E

                     mailbox[126] contiene -1
                                 e
                     mailbox[128] contiene -1

     Vediamo l’algoritmo in linguaggio naturale: con questo sistema, per
     ogni casella     0 ≤ ≤ 63 sarà necessario e sufficiente:

         • Se la casella non è vuota, fare riferimento agli offset del pezzo
           in essa contenuto.

         • Per ogni offset:

               1. Sommare il primo offset ad                    [ ]. Sia
                  l’intero così ottenuto.

               2. Se            [     ] contiene −1 fermarsi e passare
                  all’offset successivo. Se sono finiti gli offset, passare alla
                  casella successiva.

               3. Altrimenti significa che il pezzo può muoversi in
                   ℎ          [        [     ]] , a meno che questa
                  casella non sia occupata da un altro pezzo dello stesso
                  colore o che a seguito di questa mossa il re non venga
                  messo sotto scacco.

               4. Se il pezzo può muoversi di più caselle in una direzione
                  (alfiere, torre o regina) ripetere il punto 1. Altrimenti
                  passare all’offset successivo. Se sono finiti gli offset,
                  passare alla casella successiva.

     NOTA: Le colonne e le righe contenenti −1 sono due per ogni lato
     dell’array. Ne basterebbero soltanto una per lato se non fosse per il
     cavallo, il quale nel caso in cui si trovasse sul bordo di uno dei lati
     potrebbe essere spinto appunto di due colonne, o righe, fuori dalla
     scacchiera da uno dei suoi offset.

     A questo punto si può immaginare un’implementazione di questo
     algoritmo in C. Avendo già descritto l’algoritmo in linguaggio
     naturale sembra superfluo utilizzare lo pseudocodice, e più
     appropriato il C, anche per sottolineare la semplicità di calcolo
     ottenuta con la struttura dati “Mailbox Array” Siano:




28
•   “mailbox” l’array da 144 caselle.
   •   “addresser” l’array da 64 caselle.
   •    “chessboard” la scacchiera Array Based (supponiamo che il
       valore convenzionale per “casella vuota” sia zero), quindi da
       64 caselle.
   •   “color” un array da 64 caselle contenente −1, 1 o 0 a seconda
       che una casella sia occupata da un pezzo nero, bianco o vuota,
       rispettivamente.
   •   “offset” un array bidimensionale contenente in ogni colonna
       tutti gli offset per muovere ciascun pezzo (zero per i pezzi che
       hanno meno direzioni).
   •   “slide” un array di boolean che contiene “vero” nelle celle
       destinate ad alfiere, torre e regina, “falso” nelle altre.

L’algoritmo necessario a determinare tutte le caselle in cui il pezzo
contenuto nella casella “i” (da zero a 64) può essere mosso è il
seguente:

// *** ALGORITMO DI GENERAZIONE MOSSE

BOOL is_moveable = FALSE;
int n;
for (j = 0; j < offsets[piece[i]]; ++j)
      //il tipo di pezzo contenuto in piece[i]
      //ha offsets[piece[i]] offset diversi

       for (n = i;;) {
             //inizializzazione e ciclo infinito
             //”n” è la casella da esaminare

             n = mailbox[addresser[n] + offset[piece[i]][j]];

             //utilizzando le schematizzazioni di mailbox e
             //addresser sopra indicate è facile capire che
             //con questa operazione “n” diventa -1 nel caso
             //in cui il movimento usando l’offset
             //individuato nel modo descritto porti fuori
             //dalla scacchiera

             if (n == -1)
                   break;

             //se siamo usciti dalla scacchiera ci fermiamo
             //e passiamo all’offset successivo

             if (color[n] != 0) {
             //se la casella non è vuota...
                   if (color[n] == not_my_side)
                         is_moveable = TRUE;

             //se è occupata da un pezzo avversario allora



                                                                    29
//è possibile occuparla (catturando il pezzo)

                           break;
                     }
                     is_moveable = TRUE;

                     if (!slide[piece[i]])
                           break;
                     //se si tratta di alfiere, torre o regina
                     //esaminiamo un ulteriore spostamento nella
                     //direzione dell’offset preso in esame
              }

       E’ chiaro che questo algoritmo risolve il problema della generazione
       delle mosse
       Passando tutte le 64 caselle in questo modo si ha rapidamente la
       situazione della scacchiera in una determinata posizione.

       A questo punto si tratta di portare questo metodo alla nostra
       situazione di scacchiera cubica. A tal proposito, sarà sufficiente:

               1. Modificare l’array “Mailbox” in un array con 9x9x9 = 729
                  caselle, in modo da avere in ogni direzione sufficiente
                  spazio per eventuali pezzi “caduti” fuori dalla scacchiera.
                  Per ogni dimensione, infatti, avremo le 5 caselle utili più 2
                  caselle per lato (necessarie a mantenere i movimenti dei
                  cavalli)

               2. Modificare l’array “addresser” trasformandolo in un array
                  di 125 caselle. Anch’esse, come nell’esempio degli scacchi
                  classici, dovranno contenere gli indirizzi delle caselle di
                  Mailbox che non contengono “-1”.

               3. Costruire opportunamente l’array degli offset basandosi
                  sulle dimensioni di “mailbox”.

               4. Modificare gli array “chessboard” e “color” in array da 125
                  caselle.

               5. Modificare l’array “slide” aggiungendo ad esso l’unicorno.

     L’algoritmo resterà invariato e il metodo sarà applicabile al caso nostro.




30
Soluzione adottata

La soluzione più adatta al caso nostro è sicuramente “Mailbox Array”,
unendo essa la praticità della scacchiera “Array Based” senza però gli
handicap di inefficienza di cui quest’ultima soffre.
Anche la facilità di adattabilità della metodologia al nostro tipo di problema
la rende la miglior candidata fra le proposte elencate.

Esso però è difficilmente applicabile al caso del pedone: il movimento del
pedone è condizionato in modo particolare dai pezzi avversari che lo
circondano; esso infatti può muovere in diagonale soltanto se vi sono
presenti pezzi avversari nelle caselle di destinazione (per un’accurata
descrizione del movimento del pedone v. Analisi).

Basterà utilizzare l’algoritmo di generazione precedentemente individuato
solo per i pezzi che non siano pedoni, anteponendo ad esso un controllo
condizionale.
Per i pedoni sarà sufficiente controllare lo stato delle 6 caselle interessate
dal movimento di ciascuno di loro, a seconda che sia nero o sia bianco, in
modo da massimizzare l’efficienza pur scrivendo codice ridondante.

In pseudocodice:
    foreach (casella c della scacchiera)
    {
        if (color[c] == colore di turno)
        {
            if (piece[c] == pedone)
            {
                 if(color[c] == bianco)
                 {
                   //controllo dello stato delle 6 caselle
                   //corrispondenti al movimento pedone bianco
                 }
                 else
                 {
                   //controllo dello stato delle 6 caselle
                   //corrispondenti al movimento pedone nero
                 }
            }
            else
            {
                   //esegui algoritmo di generazione mosse
            }
        }
    }



I dettagli sulla effettiva implementazione sono riportati nella sezione
dedicata alla realizzazione.




                                                                            31
Tecniche di ricerca

La ricerca di una mossa in un gioco a turni si risolve, in generale, sotto
forma di ricerca in un albero.
La prima idea che viene in mente è impostare una massima profondità,
generare tutto l’albero fino a quella profondità e valutare le “foglie”, ovvero
le posizioni definite terminali.
E’ ovvio che in ogni caso la “visibilità” che l’algoritmo consente è limitata
dalla profondità impostata.
Questo fenomeno è chiamato “horizon effect”: il numero dei possibili stati, o
posizioni, è molto grande e I computer possono cercare soltanto fra una
piccola parte di essi. Perciò potrebbe capitare ad esempio che la mossa
ritenuta migliore guardando soltanto 5 mosse più avanti risulti disastrosa, al
punto di essere determinante per la perdita della partita (pensiamo ai
famosi “sacrifici”: ne è un esempio lampante la famosa partita a scacchi
detta “L’immortale”, nella quale il bianco vince pur essendo in schiacciante
inferiorità numerica).

Per questo motivo negli anni si è tentato di ottimizzare la ricerca della
mossa migliore facendo in modo di selezionare i “rami” nei quali cercare
con profondità maggiore o nei quali cercare prima.
Perciò il problema della ricerca viene a sua volta scomposto in tre problemi:

     1. Algoritmo di ricerca da utilizzare
     2. Come ottenere una differenza efficace di profondità nella ricerca
     3. In quale ordine effettuare la ricerca nei vari livelli dell’albero



Algoritmo di ricerca

Di seguito si elencano brevemente alcune fra le procedure di ricerca più
utilizzate:

     •   Minimax
         Così cita il teorema del minimax (dalla teoria dei giochi):

         per ogni gioco a somma zero con strategie finite che interessa due
         giocatori, esiste un valore V ed una strategia mista per ciascun
         giocatore tale che:

                   a. Data la strategia del giocatore 2, il miglior risultato per
                      il giocatore 1 è V.
                   b. Data la strategia del giocatore 1, il miglior risultato per il
                      giocatore 2 è –V.




32
Senza entrare ne merito della teoria dei giochi, il cui
              nel
approfondimento va al di là dello scopo di questa tesi, possiamo
applicare questo teorema agli scacchi, ed anche ai nostri scacchi 3D.

Infatti, dall’applicazione del teorema del minimax scaturisce
            l’applicazione
l’algoritmo minimax. Esso è un algoritmo ricorsivo che serve
              minimax.
generalmente a cercare la miglior mossa successiva in un gioco di n
giocatori. Viene associate un valore a ciascuna posizione o stato del
                                                               sta
gioco; questo valore indica quanto buono è per un giocatore
raggiungere q  quella posizione. Il giocatore poi farà la mossa che
massimizza il minimo valore della posizione risultante dalle possibili
mosse successive dell’avversario.


                                                                     Min




             Fig. 4 Esempio di albero minimax con valori calcolati


Il valore dei nodi viene valutato con una funzione euristica discussa e
descritta più avanti.
         a
Di seguito un’implementazione in pseudocodice dell’algortimo
maximin.

function minimax(node, depth, color)
 if ((node è un nodo terminale) or (depth == 0))
      node            terminale      depth     0
   return color * (valore euristico del nodo
                    valore               nodo)
 else
    α := -
         -∞
    foreach child of node
     α := max(α,
           max(α,-minimax(child, depth-1), -color)
                                            color)
  return α

Alla prima chiamata si setta il nodo di partenza, il colore che deve
                       setta
muovere e la profondità massima da raggiungere.




                                                                           33
•   Alfa-Beta pruning

         È un algortimo di ricerca il cui obiettivo è ridurre il numero di nodi
         da valutare rispetto all’algoritmo minimax: esso infatti smette di
         valutare una mossa quando è stata trovata almeno una possibilità in
         grado di provare che la mossa in questione è peggiore di una
         precedentemente esaminata. Questo farà sì che tale mossa non
         necessiti di ulteriore valutazione.
         E’ un’ottimizzazione che non cambia il risultato dell’algoritmo,
         quindi sicuramente da adottare.

         I benefici dell’alfa-beta pruning consistono nel fatto che
         utilizzandolo si riescono ad eliminare rami dell’albero di ricerca: in
         questo modo il tempo di ricerca può essere limitato ai rami “più
         promettenti”, permettendo così una più profonda ricerca.
         L’ottimizzazione riduce la profondità effettiva a poco più della metà
         rispetto al minimax se i nodi sono valutati in ordine ottimale o quasi
         ottimale. La dimostrazione di tale affermazione esula dagli obiettivi
         di questa tesi.

         L’algoritmo funziona mantenendo due valori, alfa e beta, che
         rappresentano il minimo punteggio ottenibile dal giocatore
         massimizzante e il massimo punteggio ottenibile dal giocatore
         minimizzante, rispettivamente. Alfa è inizialmente infinitamente
         negativo, beta infinitamente positivo. La loro distanza decresce al
         progredire della ricorsione.
         Nel momento in cui beta diventa minore di alfa significa che la
         posizione corrente non può essere il risultato del gioco ottimale di
         entrambi i giocatori, perciò diventa inutile esplorarla ulteriormente:
         è infatti inutile esplorare eventualità di gioco non ottimali nella
         speranza che esse si verifichino; questo, negli scacchi ma non solo, è
         uno dei più gravi e comuni errori dei giocatori principianti: sperare
         in un grossolano errore dell’avversario per assicurarsi la vittoria.




34
Fig. 5 - Una illustrazione di alfa-beta pruning. Esplorare i sottoalberi in grigio non
                                    beta
     migliora la soluzione (nell'esplorazione da sinistra a destra). "min" e "max"
         rappresentano i turni del giocatore e dell'avversario rispettivamente.


Pseudocodice dell’algori
             dell’algoritmo:

function alfa
         alfabeta(node, depth, α, β))
    //β rappresenta la miglior scelta della mossa
preecedente (e quindi dell’altro giocatore)
    if ((node è un nodo terminale) or (depth = 0))
         node                           depth ==
        return (valore euristico del nodo)
    foreach child of node
        α := ma
              max(α, -alfabeta(child, depth-1,
                                            1,-β,-α))
        // uso della simmetria, -β diventa α
        if β≤α
             break       // Beta cut-off
    return α

//chiamata iniziale
alfabeta(origin, depth, -infinity, +infinity)
    beta(origin,         infinity,

NOTA:
Dalla fig. 5 si nota che se i sottorami di terzo livello, figli del nodo di
              i
secondo livello marcato con valore 3, fossero scambiati di posto, uno
di loro non verrebbe nemmeno analizzato perché verrebbe tagliato
fuori dal pruning.

Infatti la tecnica di pruning può venire ulteriormente migliorata
                      p
utilizzando metodi di ordinamento euristici per individuare parti di
alberi che probabilmente potrebbero causare tagli se analizzate per
prime. Idealmente, sarebbe necessario che fossero esaminate per
prime le mos “migliori”.
           mosse

Una delle tecniche più diffuse in questo senso, per la sua efficacia e
basso costo di calcolo, è la cosiddetta killer heuristic.




                                                                                     35
•   Killer Heuristic
         Si tratta di una tecnica di ordinamento di alberi in supporto
         all’algoritmo di alfa-beta pruning.
         Si tenta di produrre un cutoff sperando che una mossa che ha
         prodotto un cutoff in un altro ramo dell’albero alla stessa profondità
         produca un cutoff nella posizione in cui ci si trova al momento.
         Particolarmente efficace negli scacchi, dove spesso una mossa buona
         si rivela tale anche in posizioni di poco diverse. Perciò, esplorando la
         mossa killer prima di altre mosse spesso si riesce a creare un taglio, il
         cui beneficio sarà tanto maggiore quanto minore sarà la profondità in
         cui lo si esegue.

         History Heuristic
         Ulteriore evoluzione, di comprovata efficacia negli scacchi classici, è
         la cosiddetta history heuristic.
         Si costruisce una tabella di mosse, indicizzandole in qualche modo,
         ad esempio tramite il pezzo che muove e la casella di destinazione. Al
         verificarsi di un taglio l’elemento appropriato della tabella viene
         incrementato, ad esempio aggiungendo ad esso la profondità
         rimanente (quindi mosse meno profonde risulteranno avere
         punteggio più alto).
         In questo modo si tenderà a privilegiare, fra le mosse che hanno
         causato tagli, quelle avvenute in rami meno profondi dell’albero,
         quindi in grado di produrre tagli più cospicui.

         Qualunque sia l’algoritmo di ordinamento utilizzato, questo dovrà
         avvenire prima della ricerca sull’albero.

     •   Quiescenza

         Questo metodo di ricerca si è provato funzionale sperimentalmente,
         e deriva da un’imitazione di comportamento umano: un giocatore
         infatti tende istintivamente ad analizzare più in profondità situazioni
         “agitate”, ovvero mosse attraverso le quali la posizione viene
         profondamente alterata; tipicamente, mosse di cattura.

         Il metodo consiste semplicemente nel dedicare maggiore profondità
         all’analisi di mosse di cattura.

     •   Principal Variation + Iterative Deepening

         Il termine variazione si riferisce ad una specifica sequenza di mosse
         in un gioco a turni generico; spesso è utilizzato per identificare un
         ipotetico stato futuro del gioco.




36
La variazione principale (principal variation) è quella particolare
variazione che è la più vantaggiosa per il giocatore corrente,
assumendo che il giocatore avversario risponda con le mosse per lui
di volta in volta migliori. In altre parole essa è la “migliore” o
“corretta” linea di gioco.
Nel contesto qui preso in esame si indica con variazione principale la
sequenza che il giocatore computerizzato crede essere la migliore;
questo assunto non è garantito a causa dell’euristicità dell’algoritmo
di valutazione e dell’horizon effect.




       Fig. 6 In blu la variazione principale di un ipotetico albero minimax


Estendendo ancora il significato del termine, si può pensare di avere
una variazione principale per ciascun grado di profondità dell’albero:
ciascuna di esse sarebbe lunga quanto la profondità esaminata.

Utilizzare un array bidimensionale quadrato (avente come “lato” la
massima profondità che si decide di analizzare), unito ad uno
schema iniziale di iterative deepening, ovvero un ciclo che richiama
l’algoritmo di alfa-beta pruning con limite di profondità inizialmente
pari a 1 e ad ogni iterazione incrementato fino ad un certo massimo,
permette di mantenere la “storia” delle soluzioni trovate e
l’evoluzione della soluzione migliore trovata verso l’ottimalità
attraverso i vari passaggi all’interno dell’albero.




                                                                               37
Soluzione adottata

Per la sua comprovata capacità di migliorare l’efficienza rispetto al
Minimax, l’algoritmo che si utilizzerà sarà l’alfa-beta pruning, corredato da
ricerca di quiescenza a profondità maggiore.
Inoltre, si utilizzerà una tabella per l’implementazione dell’algoritmo
“History Heuristics”.

In questo modo si potrà efficientemente compiere ricerche in profondità
nell’albero, potendo così supplire alle carenze (inevitabili) della funzione di
valutazione della posizione.

Si è desciso di utilizzare una ricerca a tre livelli. Riferendosi alla figura 7:
una prima funzione, think, richiama l’algoritmo alfa-beta pruning, chiamato
search, dando ad esso profondità limite inizialmente pari ad 1 e ad ogni
iterazione incrementata di 1.
Questo è necessario per soddisfare il requisito della limitazione temporale
nella nostra ricerca: alfa-beta pruning è un algoritmo di tipo depth-first;
questo significa che non si ha soluzione fino a che non è stato esplorato
tutto l’albero alla massima profondità desiderata.
Invece, impostando la profondità massima di volta in volta maggiore, ed
utilizzando un array per contenere tutte le variazioni principali ottenute
nelle varie iterazioni, l’algoritmo di ricerca potrà essere interrotto anche
dopo brevissimo tempo: in caso di interruzione verrà utilizzata la miglior
soluzione trovata fino a quel momento, indipendentemente da quanto in
profondità si sia riusciti ad andare.

Ad ogni iterazione viene chiamata la search, che implementa alfa-beta
pruning. Essa genera tutte le mosse possibili nella posizione corrente e,
iterando sull’elenco che le contiene, ne esegue la i-esima, richiama sé stessa
(invertendo alfa e beta perché il punteggio del giocatore nero è opposto a
quello del giocatore bianco) riducendo la profondità rimanente (depth),
annulla la mossa i-esima. Poi controlla il valore restituito dalla chiamata
ricorsiva, e, se è avvenuto un cut-off, aggiorna la tabella per history
heuristics, aggiorna l’array delle variazioni principali, e aggiorna il valore di
alfa.
Proseguendo con la ricorsione, la profondità rimanente (depth) descresce
fino ad arrivare a zero. A quel punto viene richiamata un’altra funzione che
implementa l’alfa-beta pruning, chiamata quiesce. Essa, a differenza di
search, genera soltanto mosse di cattura; così facendo si è implementata la
ricerca estesa per situazioni dove il punteggio della posizione può variare di
molto (per l’appunto nel caso di catture di pezzi).
In essa avviene anche la chiamata alla funzione di valutazione della
posizione.




38
Con il sistema così progettato si può ottenere grande profondità di calcolo
nell’esplorazione della posizione nel caso il tempo concesso al computer sia
elevato e contemporaneamente una risposta in breve tempo nel caso si
volesse dare al software soltanto pochi secondi per decidere.




                                                                         39
Fig. 7 -Algoritmo semplificato di ricerca della mossa migliore




40
Valutazione di una posizione

Questo, dei tre problemi è sicuramente il più arduo: come valutare la bontà
di una posizione? Molte sono le strategie di calcolo utilizzate negli scacchi
normali. In ogni caso, la valutazione avviene con un algoritmo molto
specifico, che funziona soltanto per lo speciale gioco degli scacchi classici.
Tuttavia, alcuni fattori di cui si tiene conto negli scacchi possono essere
utilizzati anche nel nostro caso.
Sicuramente saranno sufficienti a giungere ad una soluzione iniziale
soddisfacente, in modo che l’utente percepisca un certo grado di
intelligenza nella scelta delle mosse da parte del computer, in quanto sono
le più influenti anche nelle scacchi classici.

In ogni caso si tratta sempre di algoritmi euristici, per i quali non riusciamo
nemmeno a stimare il grado di ottimalità della soluzione trovata.

Vediamo gli accorgimenti applicabili.

Alternative progettuali

   •   Bilanciamento degli schieramenti

       Il modo più intuitivo per assegnare un punteggio ad una posizione è
       contare quanto “materiale”, nel senso di pezzi, è ancora disponibile al
       bianco e quanto al nero.
       Inizialmente, entrambi dispongono di dieci pedoni, due unicorni,
       due cavalli, due alfieri, due torri, una regina, e naturalmente un re.
       La “potenza” di un pezzo è determinata unicamente dal suo grado di
       mobilità.
       La mobilità dipende sia da come esso può intrinsecamente muoversi
       (per regolamento) sia dalla sua posizione sulla scacchiera.




                                                                             41
Vediamo in una tabella le “potenzialità” di ciascun pezzo:

                   Direzioni          Caselle
     Pezzo        Al       Sui     Al        Sui   Slide                        Note
                centro vertici   centro vertici
                                                           Può muoversi in un ristretto numero di caselle
Unicorno          8       1        8        4       Sì
                                                                 rispetto le 125 totali (vedi fig.8)
                                                           La sua mobilità è ostacolata soltanto da pezzi
 Cavallo         24       8       24        8      No      presenti sulle caselle di destinazione e non da
                                                               quelli sul tragitto (“salta” oltre i pezzi).
                                                              Può muoversi su metà delle caselle totali
 Alfiere         12       3       32       12       Sì
                                                            (quelle del colore della sua casa di partenza)
                                                           Può muoversi sempre al massimo in 12 caselle,
     Torre        6       3       12       12       Sì
                                                            a meno che non vi siano ostacoli sul tragitto.
                                                           Ad ogni mossa possiede le mobilità addizionate
 Regina          26       7       52       28       Sì
                                                             di un alfiere, di una torre e di un unicorno.
                                                             Come tutti i pezzi è molto mobile al centro
                                                           della scacchiera. Tuttavia è opportuno tenerlo
      Re         26       7       26        7      No
                                                           “in salvo” in quanto la sua perdita comporta la
                                                              sconfitta. Il suo valore sarà quindi infinito.


             Il pedone non è qui trattato, essendo un pezzo “speciale” sotto molti
             punti di vista.
             Alcuni chiarimenti sulla tabella: con “slide” stiamo ad indicare se un
             pezzo può “scivolare” di più caselle in una direzione, nel modo in cui
             lo fanno appunto l’alfiere, la torre, la regina e l’unicorno. Nella
             tabella si è tentato di quantificare intuitivamente in prima
             approssimazione la mobilità dei pezzi, confrontando il numero di
             direzioni e il numero di caselle in cui ciascun pezzo può muoversi nel
             caso esso si trovi su una scacchiera vuota in un vertice oppure al
             centro.
             Risulta immediatamente chiaro che alcuni pezzi sono sempre più
             mobili di altri, e tutti traggono beneficio quando sono spostati verso
             il centro della scacchiera.

             Ad esempio, è chiaro che l’unicorno è sicuramente il pezzo di minor
             valore fra quelli segnati in tabella: non solo non è molto mobile, ma
             non può nemmeno viaggiare su tutta la scacchiera, bensì solo su una
             minima parte di essa (circa ¼ delle caselle).
             Per contro, la regina è sicuramente il pezzo che vale di più
             (ricordiamo che il re ha valore idealmente infinito, quindi non
             conta), in quanto essa è il pezzo più mobile di tutti.




42
Fig. 8 - I 4 “tipi” di unicorno diversi e il dettaglio delle loro mobilità.


La cosa più difficile nella valutazione del materiale a disposizione è
quantificare il valore dei singoli pezzi; negli scacchi classici si sono
fissati, nei secoli di esperienza, alcuni valori di riferimento:

   o   Pedone:           1
   o   Cavallo:          3
   o   Alfiere:          3
   o   Torre:            5
   o   Regina:           9

In realtà vi sono opinioni discordanti, spesso all’alfiere viene
attribuito un valore maggiore che al cavallo. Poi verso il finale,
ovvero quando ad un certo punto i pezzi in gioco cominciano ad
essere “pochi”, i valori cambiano.

Inoltre, il valore dei pedoni negli scacchi classici varia fortemente a
seconda della posizione del pedone: se infatti esso può facilmente
essere portato in zona di promozione varrà la pena difenderlo anche
a costo di perdere ad esempio una torre ed un alfiere, per guadagnare
una regina.




                                                                                 43
Indipendentemente da tutto ciò, analizzando la nostra variante 3D, ci
         si accorge che i valori non vi si adattano: la torre, ad esempio, non è
         così “potente” come negli scacchi, perde infatti nella terza
         dimensione la sua funzione di “muro”; inoltre non è più così efficace
         nel sostegno dei pedoni.

         Lungi dagli obiettivi di questa tesi eseguire un approfondito studio
         sul punteggio base da assegnare ai pezzi, si è immaginata una sorta di
         “traduzione” dagli scacchi classici agli scacchi 3D, successivamente
         spiegata.

     •   Sviluppo

         Il valore di un pezzo è tanto maggiore quanto maggiore è la sua
         influenza sulla scacchiera. Ad esempio una torre nella sua posizione
         originale non “controlla” nessuna casella, ovvero non può muoversi
         perché bloccata da altri pezzi del suo stesso schieramento.
         I pezzi hanno massima influenza quando sono mossi verso il centro
         della scacchiera.

         Per questi motivi, negli scacchi classici si costruiscono array di interi
         di dimensioni della scacchiera, i cui valori corrispondono a piccoli
         incrementi o decrementi di punteggio da applicare ai pezzi a seconda
         della loro posizione.

         In questo modo una scacchiera con pezzi sviluppati varrà di più
         rispetto ad una con i pezzi bloccati nelle posizioni originali.

         Nel caso nostro sarà semplice adattare una simile tecnica, non
         essendo obiettivo di questa tesi individuare i numeri ottimali per
         ottenere la valutazione più precisa: sarà sufficiente un array con
         somma zero e valori tanto cospicui da fare la differenza rispetto ad
         un metodo di valutazione privo di questo accorgimento.

     •   Struttura dei pedoni

         Negli scacchi classici, in due dimensioni, risulta abbastanza semplice
         e intuitivo modificare il valore dei pedoni in funzione delle loro
         posizioni reciproche.
         Ad esempio, se in una colonna vi sono due o più pedoni dello stesso
         colore è naturale pensare che essi si ostacoleranno. Al comparire
         della terza dimensione tuttavia questo fenomeno tende praticamente
         a scomparire, muovendosi essi non solo lungo le colonne ma anche
         attraverso i piani.




44
Omettiamo quindi di descrivere le varie strategie utilizzate per il
    calcolo del valore della struttura dei pedoni negli scacchi classici,
    perché esse non sarebbero adottabili per nostri scacchi 3D.

•   Posizione del re

    Negli scacchi classici, durante l’apertura (inizio della partita) e il
    mediogioco (parte del gioco difficilmente definibile, in essa i pezzi
    sono sviluppati e sono ancora “tanti”), il re è bene che stia dove si
    trova all’inizio, o ancora meglio “arroccato” ad uno dei due lati.
    Negli scacchi 3D non vi è manovra di arrocco, quindi è consigliabile
    che all’inizio esso rimanga nei pressi della casella iniziale, anche
    perché sarebbe inutile sprecare preziose mosse di sviluppo per
    spostare il lento re e portarlo alla mercé dell’esercito avversario.

    Nel finale della partita, quando i pezzi sono “pochi”, è essenziale,
    negli scacchi classici, che il re partecipi attivamente al
    combattimento.
    Infatti, in tutti i finali fondamentali (re e regina contro re, re e torre
    contro re, re e pedone contro re, ...) esso gioca un ruolo
    fondamentale.
    Di uguale importanza, se non maggiore, sarà negli scacchi 3D, nei
    quali è ancora più arduo, a causa della tridimensionalità, dare lo
    scacco matto.

    E’ quindi utile individuare un momento della partita oltre il quale il
    re “vale” di più se portato verso il centro dela scacchiera.
    La metodologia più semplice da utilizzare è valutare il materiale a
    disposizione dell’avversario: nel momento in cui i pezzi sono troppo
    pochi per nuocere seriamente, allora il re potrà essere portato allo
    scoperto.




                                                                           45
Soluzione adottata

           Bilanciamento degli schieramenti
           Anzitutto è opportuno fissare dei valori di base a ciascun tipo di
           pezzo. Ciò può essere fatto nel seguente modo: si possono definire i
           suddetti valori rapportando i gradi di mobilità dei pezzi negli scacchi
           classici con quelli degli stessi nel 3D, e variare il loro valore
           proporzionalmente.

           Nella tabella seguente si compara la mobilità di cavallo, alfiere, torre
           e regina negli scacchi 3D e negli scacchi classici.

                             3D Chess                                      Scacchi Classici
 Pezzo            Direzioni          Caselle visitabili          Direzioni             Caselle visitabili
            Al centro Sui vertici Al centro Sui vertici    Al centro Sui vertici Al centro Sui vertici

Cavallo        24          8         24           8           8            2           8           2


 Alfiere       12          3         32          12           4            1          13           7


 Torre         6           3         12          12           4            2          14          14


 Regina        26          7         52          28           8            3          27          21



           Nella prossima tabella si riportano soltanto le caselle visitabili,
           rapportate però al numero di caselle totali della scacchiera (125 nel
           3D, 64 negli scacchi classici).

                                         Caselle visitabili / Caselle totali
                         Pezzo          3D Chess                Scacchi Classici
                                  Al centro Sui vertici Al centro Sui vertici

                        Cavallo    0.192       0.064        0.125       0.031


                        Alfiere    0.256       0.096        0.203       0.109


                         Torre     0.096       0.096        0.219       0.219


                        Regina     0.416       0.224        0.422       0.328



           Si nota subito che il cavallo 3D risulta molto più mobile di quello
           classico. Al contrario, la torre è sensibilmente meno mobile, come ci
           si aspettava.




46
Si osservi ora nella tabella seguente l’aumento di mobilità in
          prossimità dei vertici e in posizione centrale di ciascun pezzo dagli
          scacchi classici agli scacchi 3D:

                                     Fattore di incremento di mobilità nel 3D
Pezzo            Direzioni            Caselle visitabili         Caselle visitabili/Caselle totali
           Al centro Sui vertici   Al centro Sui vertici        Al centro                Sui vertici

Cavallo       3           4           3            4               1.536                    2.064


Alfiere       3           3         2.286        1.714             1.261                    0.881


Torre        1.5         1.5        0.857        0.857             0.438                    0.438


Regina       3.25       2.333       1.857        1.333             0.986                    0.683



          A questo punto, applicando a questi fattori di incremento e ai valori
          negli scacchi classici un’opportuna funzione, sarà possibile ottenere
          dei valori da sperimentare nel motore per questi pezzi.

          Sicuramente il fattore di peso maggiore sarà il rapporto tra caselle
          visitabili e caselle totali, esso esprime la differenza di mobilità meglio
          rispetto agli altri fattori, essendo relativo.

          Un buon esempio potrebbe essere dare 10% di peso agli incrementi di
          direzioni, 14% di peso agli incrementi assoluti di caselle visitabili e
          26% a quelli relativi.

          Perciò, definiti con

             e      gli incrementi assoluti di direzione rispettivamente al centro e ai vertici
             e      gli incrementi assoluti di caselle rispettivamente al centro e ai vertici
              e       gli incrementi relativi di caselle rispettivamente al centro e ai vertici

          il fattore selezionato è:
                 = [10 ∗ ( +        ) + 14 ∗ (             +      ) + 28 ∗ (         +       ) ]/100

          Esso andrà poi moltiplicato per il valore convenzionale del pezzo
          negli scacchi classici.

          Sostituendo i valori numerici si ottiene:

             o      Cavallo:              = 2.616        segue punteggio: 2.616 ∗ 3 = 7.848
             o      Alfiere:              = 1.717        segue punteggio: 1.717 ∗ 3 = 5.151
             o      Torre:                = 0.768        segue punteggio: 0.768 ∗ 5 = 3.839
             o      Regina:               = 1.439        segue punteggio: 1.439 ∗ 9 = 12.95


                                                                                                       47
I valori così ottenuti saranno utilizzati come punteggio base dei
     pezzi. Mancano ancora tuttavia Unicorno e Pedone.

     Per quanto riguarda l’Unicorno, esso ha una mobilità che oscilla tra
     circa (sui vertici) e circa (al centro) rispetto a quello della regina
     (in base alle tabelle stilate). Inoltre, può muoversi al massimo su
     circa ¼ della scacchiera; questo fa di esso un pezzo estremamente
     debole e poco prezioso.

     Fissiamo il valore dell’unicorno a rispetto a quello della regina, cioè
     2.16.

     Per i pedoni non si hanno parametri di valutazione da cui partire, se
     non il fatto che negli scacchi classici essi valgono 1 punto. Fissiamo il
     loro valore a 1 anche negli scacchi 3D.
                                                                 Pezzo    valore
     Si riassume nella tabella a fianco i valori di base che   Pedone       1
     si è deciso di adottare, arrotondati ad una cifra         Unicorno    2.2
                                                                Cavallo    7.8
     decimale: sarebbe inutile essere molto precisi in
                                                                Alfiere    5.1
     quanto si tratta di valori praticamente arbitrari.          Torre     3.8
                                                                Regina     12.9


     Sviluppo
     I pezzi aumentano di valore quando vengono sviluppati, cioè
     sostanzialmente portati verso il centro della scacchiera. Questo vale
     quasi per tutti, a parte per i pedoni.
     Essi infatti valgono tanto quanto più si avvicinano alla zona di
     promozione.

     Il modo più semplice ed efficiente per ottenere questo effetto è
     utilizzare un array di interi grande quanto la scacchiera, inserendo
     un numero negativo nelle caselle “cattive” (vertici e bordi) e un
     numero positivo nelle caselle “buone” (centrali).
     Per ogni pezzo mosso si sommerà al suo valore il numero
     corrispondente alla casella in cui lo si vuole muovere.
     Questi accorgimenti si rivelano utili specialmente nella fase iniziale
     del gioco.

     Posizione del re
     Al re assegneremo in fase di realizzazione un valore
     incommensurabilmente alto rispetto a quello degli altri pezzi,
     naturalmente, perché perderlo equivale a perdere la partita.




48
Il re necessita anche di due array di interi grandi come la scacchiera:
      uno per l’inizio della partita, per fare in modo che esso tenda a
      rimanere nella posizione iniziale, e uno per il finale.
      Si entrerà nel “finale” quando il valore dei pezzi avversari sarà uguale
      o inferiore a 1300, re escluso.



Riepilogo funzionamento globale




                  Fig. 9 - comportamento macroscopico del motore


In figura 9 è illustrato il comportamento del motore dal punto di vista
macroscopico: la UI comunica al motore la posizione della scacchiera,
intesa come stato della partita. Se valida, si generano le mosse possibili. Se
ne esistono, si ricerca la mossa migliore, e la si comunica alla UI.




                                                                           49
Progettazione dell’Interfaccia


Scacchiera Tridimensionale
Essa è la componente principale dell’interfaccia utente.
Vale la pena stendere una breve introduzione sull’argomento grafica
tridimensionale.

La grafica tridimensionale
La computer grafica 3D è un ramo della computer grafica che si basa
sull'elaborazione di modelli virtuali in 3D da parte di un computer. Essa
viene utilizzata insieme alla computer animation nella realizzazione di
immagini visuali per cinema o televisione, videogiochi, ingegneria, usi
commerciali o scientifici. Il termine può anche essere riferito ad immagini
prodotte con lo stesso metodo.

La grafica computerizzata tridimensionale è basilarmente la scienza, lo
studio e il metodo di proiezione della rappresentazione matematica di
oggetti tridimensionali tramite un'immagine bidimensionale attraverso
l'uso di tecniche come la prospettiva e l'ombreggiatura (shading) per
simulare la percezione di questi oggetti da parte dell'occhio umano. Ogni
sistema 3D deve fornire due elementi: un metodo di descrizione del sistema
3D stesso ("scena"), composto di rappresentazioni matematiche di oggetti
tridimensionali, detti "modelli", e un meccanismo di produzione di
un'immagine 2D dalla scena, detto "renderer".

Modelli 3D
Oggetti tridimensionali semplici possono essere rappresentati con
equazioni operanti su un sistema di riferimento cartesiano tridimensionale:
per esempio, l'equazione x²+y²+z²=r² è perfetta per una sfera di raggio r.
Anche se equazioni così semplici possono sembrare limitative, l'insieme
degli oggetti realizzabili viene ampliato con una tecnica chiamata geometria
solida costruttiva (CSG, constructive solid geometry), la quale combina
oggetti solidi (come cubi, sfere, cilindri, ecc.) per formare oggetti più
complessi attraverso le operazioni booleane (unione, sottrazione e
intersezione): un tubo può ad esempio essere rappresentato come la
differenza tra due cilindri aventi diametro differente.

L'impiego di equazioni matematiche pure come queste richiede l'utilizzo di
una gran quantità di potenza di calcolo, e non sono quindi pratiche per le
applicazioni in tempo reale come videogiochi e simulazioni. Una tecnica più
efficiente, ma che permette un minore livello di dettaglio, per modellare
oggetti consiste nel rilevare solo alcuni punti dell'oggetto, senza
informazioni sulla curva compresa tra di essi. Il risultato è chiamato




50
modello poligonale. Questo presenta "faccette" piuttosto che curve, ma
sono state sviluppate tecniche d rendering per ovviare a questa perdita di
                                 di
dati.
Delle superfici poligonali di un modello senza informazioni sulla curvatura
possono essere comunque raffinate per via algoritmica in superfici
perfettamente curve: questa tecnica è chiamata "superfici di su   suddivisione"
(subsurfing), perché la superficie viene suddivisa con un processo iterativo
            ,
in più superfici, sempre più piccole, fedeli alla curva interpolata e che vanno
a comporre un'unica superficie sempre più liscia.

Creazione della scena
Una scena si compone di "primitive" (modelli tridimensionali che non
                 ompone
possono essere ulteriormente scomposti).
Le primitive sono generalmente descritte all'interno del proprio sistema di
riferimento locale, e vengono posizionate sulla scena attraverso opportune
trasformazioni. Le trasformazioni affini più impiegate, come omotetia,
          zioni.
rotazione e traslazione, possono essere descritte in uno spazio proiettivo
con una matrice 4x4: esse si applicano moltiplicando la matrice per il
vettore a quattro componenti che rappresenta ogni punto di controllo delle
                                               ogni
curva. La quarta dimensione è denominata coordinata omogenea.

Rendering
Il rendering è il processo di produzione dell'immagine finale a partire dal
modello matematico del soggetto (scena). Esistono molti algoritmi di
rendering, ma tutti implicano la proiezione dei modelli 3D su una superficie
2D.
Gli oggetti tridimensionali devono essere proiettati idealmente sulla
superficie bidimensionale del monitor.
Il tipo di proiezione p usato è la prospettica, anche se ad esempio nei
                       più                       ,
sistemi CAD e CAM si utilizza la proiezione ortogonale.
  stemi
La differenza fra le due è che nella prospettica gli oggetti più lontani sono
visualizzati di dimensioni minori rispetto a quelli più vicini all’ “occhio”. I
                           minori
software rendono la prospettiva moltiplicando una costante di dilatazione k
                                 moltiplicando
elevata all’opposto della distanza dall’osservatore. Se k = 1 non vi è
prospettiva. All’aumentare di k aumenta la distorsione prospettica.

                                          Fig. 10




                  Un punto di fuga - Due punti di fuga - Tre punti di fuga


Una trattazione dettagliata della matematica riguardande la proiezione
prospettica esula dagli scopi di questo testo.


                                                                             51
Per una spiegazione intuitiva è sufficiente immaginare una videocamera che
si muove all’interno di uno spazio tridimensionale. In questo spazio sono
contenuti i modelli tridimensionali, nel nostro caso si tratta della scacchiera
e dei pezzi.
L’utente sarà in grado di controllare il “movimento” di questa videocamera
ideale all’interno dello spazio 3D in modo tale da poter visualizzare
comodamente.




                 Fig. 11 - Field of view (campo visivo) della videocamera


In figura 11 è descritto il campo visivo che viene sottoposto all’utente: è di
forma tronco-conica (frustum), tutto ciò che non è contenuto in esso non
viene proiettato (sulla base minore, che rappresenta la superficie dello
schermo) e quindi non viene visualizzato.


Progettazione dei modelli

Vi sono principalmente quattro modi per rappresentare un modello 3D:

     •   Modelli poligonali – Un insieme di punti nello spazio
         tridimensionale, detti vertici, vengono connessi da segmenti in modo
         da formare una maglia (mesh). La maggior parte dei modelli 3D sono
         costruiti in questo modo per la loro flessibilità e velocità di
         rendering. Tuttavia, i poligoni sono piani, quindi con essi le
         curvature possono soltanto essere approssimate utilizzando un gran
         numero di poligoni.




52
•    NURBS – Le superfici NURBS sono definite da curve spline, cioè
        parametrizzate da punti di controllo pesati. La curva segue (senza
        necessariamente interpolarli) i punti. Aumentando il peso di un
        punto si farà in modo che la curva passi più vicino ad esso. In questo
        modo si riescono ad ottenere superfici estremamente lisce.

   •    Splines & Patches – Come le NURBS, splines and patches dipendono
        da line curve per definire la superficie visibile. Sono una via di mezzo
        fra NURBS e poligoni in termini di flessibilità e facilità di utilizzo.

   •    Primitives modeling – Consiste nell’utilizzare primitive geometriche
        come sfere, cilindri, coni o cubi, come “mattoni” per modelli più
        complessi. Si ottengono così costruzioni veloci, e le forme sono
        matematicamente       precise.   Richiede     però     di   definire
        geometricamente il modello da costruire; ciò non è sempre possible
        o facile da ottenere.

Nel nostro caso, trattandosi di pezzi degli scacchi, quindi di forme
geometriche non troppo complesse, i metodi migliori sarebbero modelli
poligonali o primitives modeling. Quest’ultimo sembrerebbe il più indicato,
tuttavia, sebbene sia controintuitivo, i modelli ottenuti sarebbero
certamente più grezzi: modellare tutto tramite primitive, infatti,
implicherebbe poter utilizzare soltanto curve e forme definibili
geometricamente. Invece, tramite poligoni, si avrà più rapidità al momento
della costruzione dei modelli (si potranno comporre facilmente tramite una
serie di punti); l’arrotondamento delle superfici si potrà ottenere per
suddivisioni successive, mantenendo inoltre la massima compatibilità (i
formati poligonali sono i più comuni al momento).

Pezzi
Consisteranno in sette modelli poligonali, ciascuno per un tipo di pezzo. Il
colore e il posizionamento sulla scacchiera verranno dati loro dal
programma di gioco.


Scacchiera
Per costruirla si utilizzerà un mattoncino fondamentale (un parallelepipedo
molto esteso in larghezza rispetto alla sua altezza, una sorta di
“mattonella”), ripetuto 125 volte a colorazione alterna.
Si visualizzeranno cioè cinque scacchiere piane sovrapposte, come in figura
1.




                                                                             53
Interazione con l’utente


Mouse – Movimentazione dei pezzi

Per muovere i pezzi si deve poter, tramite mouse, selezionare il pezzo da
muovere e successivamente selezionare la casella in cui muoverlo. In figura
12 è illustrato l’algoritmo ad alto livello per la selezione del pezzo.



                               L’utente fa click sulla
                             schermata dell’interfaccia
                                         3D



                                         No




                                 Ha fatto click su un
                                                                                   Sì
     Attesa di                         pezzo?
interazione utente


                                         No




       No                       Ha fatto click su una
                                                                                            Il pezzo è suo?
                                      casella?




       No                                Sì                                                        Sì



                                   Al momento è
                                                                                              Il pezzo può
                                   evidenziato un
                                                                                                muovere?
                                       pezzo?                      Sì



                                                                                                   Sì


                  Togliere l’evidenziazione
                                                                                        Evidenziare il pezzo e le
                 al pezzo precedentemente
                                                              La casella è               caselle in cui esso può
                 selezionato ed alle caselle   No
                                                              evidenziata?                      muovere
                  in cui esso può muovere
                                                                                           (Selezione Pezzo)
                    (Deselezione Pezzo)


                                                                   Sì



                                                          Muovere il pezzo nella
                                                          casella (Spostamento
                                                                  Pezzo)



              Fig. 12 - Flow chart raffigurante il loop di input da mouse per l'utente




54
Il problema fondamentale però è che l’utente fa click su un punto di una
superficie piana; a partire dalle coordinate piane del click bisogna risalire a
quale punto dello spazio esso corrisponde.

I modelli che posizioniamo nello spazio sono costituiti da triangoli. Si
tratterà quindi di determinare, ciclando su tutti i modelli contenuti nello
spazio, quale di essi è il più vicino alla camera fra quelli intersecati dalla
semiretta avente origine nel punto in cui l’utente ha fatto click.

I modelli sono composti da triangoli. Bisognerà quindi procedere in questo
modo:

    1. Per ogni modello controllare se la sfera di raggio minimo che lo
       contiene tutto interseca la semiretta in questione.
    2. Se la sfera è intersecata bisognerà testare, triangolo per triangolo, se
       vi sono triangoli componenti il modello che intersecano la
       semiretta.
    3. Se ve ne sono, salvare quello di distanza minima dalla camera. Se
       minore di quelli precedentemente trovati, memorizzare il modello a
       cui appartiene.

In questo modo si troverà il modello più vicino alla camera intersecato dalla
semiretta.

Il problema più complesso è individuare quali triangoli di un modello,
ammesso che ve ne siano, siano intersecati dalla semiretta.
Un buon sistema per ottenere questo risultato è utilizzare un algoritmo il
cui uso è abbastanza diffuso per casi come questo, ideato da Tomas Moller e
Ben Trumbore nel 2003 chiamato “Fast, Minimum Storage Ray-Triangle
Intersection".

Questo algoritmo permette, senza perdita di velocità rispetto agli algoritmi
precedenti, di risparmiare memoria nel suo utilizzo.

In appendice si allega il documento originale che sarà utilizzato per
implementare l’algoritmo nella parte di realizzazione.


Tastiera – Camera

Per far in modo che il gioco sia il più controllabile possibile, si progettano
due modalità di visualizzazione (camera).
Una il prima persona, che permette cioè di spostare liberamente la camera
per mezzo della tastiera nello spazio contenente la scacchiera.



                                                                            55
Un’altra invece vincolata alle righe e alle colonne della scacchiera: ovvero,
essendovi, 5 righe e 5 colonne per ciascuno dei 5 piani, con (5 + 5) ∗ 5 = 50
posizioni; ad ogni spostamento di posizione la si farà puntare
parallelamente alla direzione della riga o colonna, ma sarà possibile ruotarla
sul posto per facilitare la visualizzazione.

L’utente potrà in qualsiasi momento cambiare camera passando da totale
libertà di movimento a controllo più vincolato.

Comunicazione Interfaccia-Motore

Differenza sostanziale tra le realizzazioni di Interfaccia e Motore sarà   la
tecnologia d’implementazione; per la prima è necessaria una piattaforma    in
grado di supportare la grafica 3D e che renda il più possibile agevole     lo
sviluppo di videogame, mentre per il secondo è importante l’efficienza     di
calcolo.

Perciò, per l’Interfaccia sarà certamente opportuno utilizzare una API per il
game-programming. Ve ne sono tante disponibili, ma non molte soddisfano
il requisito della portabilità che è stato richiesto (vedi Analisi, pag. 17).

Per quanto riguarda il Motore sarà più utile una realizzazione che non giri
su macchina virtuale; in questo modo si avrà il massimo dell’efficienza.
Quindi un linguaggio compilato.
Per soddisfare la portabilità basterà usare un linguaggio molto diffuso, in
modo che esistano compilatori per esso almeno per le piattaforme più
utilizzate; certo, ci sarà bisogno di ricompilare per passare da un ambiente
ad un altro, ma il requisito di efficienza è certamente più importante per
ottenere un Motore “intelligente” in grado di tener testa al giocatore umano.

La comunicazione fra i due è sufficiente che sia in una sola direzione, cioè
dall’Interfaccia al Motore, o vice versa; non ponendo vincoli in questo
frangente si lascia maggior libertà in fase di realizzazione.




56
Realizzazione

Tecnologie utilizzate
Microsoft XNA (Xbox New Architecture)
Si tratta di un inseme di tool inseriti in un ambiente di runtime “managed”
il cui scopo è facilitare lo sviluppo di videogame sollevando lo sviluppatore
dall’onere di dover scrivere codice ripetitivo. Comprende:

   •   XNA Framework
       Si basa sull’implementazione del .NET Compact Framework 2.0 per
       Xbox 360 e su .NET Framework 2.0 per Windows. E’ disponibile per
       Windows XP, Windows Vista e Xbox 360; permette di scrivere i
       videogame per il runtime di XNA: questo fa sì che essi possano girare
       su qualsiasi piattaforma a patto che questa supporti il runtime.

L’utilizzo di questa tecnologia quindi ci permetterebbe di portare senza
troppe difficoltà il gioco su piattaforme diverse, e ci solleva inoltre
dall’onere di reinventare ciò che nel gergo del game development si
definisce “game loop” (e tutto ciò che esso implica).


   •   XNA Build
       E’ un insieme di tool per la gestione degli “asset” di gioco (icone, file
       audio, modelli 3D, ...). Aiuta a definire la cosiddetta game asset
       pipeline. Quest’ultima descrive il processo attraverso il quale i
       contenuti vengono trasformati in forme utilizzabili dall’engine del
       gioco. XNA Build permette di personalizzare a vari livelli ogni stadio
       della suddetta pipeline.


   •   Mono.XNA
       E’ una implementazione cross platform di XNA gaming framework.
       Prende il nome da Mono, ormai nota implementazione cross
       platform di .NET framework
       Gira su Windows, MacOS e Linux e usa OpenGL per la grafica 3D.
       Al momento è ancora in fase di realizzazione.

Questo aspetto di XNA ci assicura la portabilità richiesta nei requisiti non
funzionali.

XNA sarà quindi utilizzato per la realizzazione dell’Interfaccia.




                                                                             57
Blender
E’ un’applicazione per grafica 3D rilasciata sotto forma di software libero
sotto GNU General Public License. Può essere usato per modeling e molto
altro ancora, ed è disponibile per Linux, Mac Os X e Windows.
Questo lo rende più che sufficiente per lo scopo di questa tesi.

Con Blender saranno costruiti i modelli 3D di pezzi e scacchiera.


ANSI C
E’ lo standard emesso dall’American National Standards Institute (ANSI)
per il linguaggio di programmazione C.
ANSI C è supportato praticamente da tutti i compilatori più utilizzati;
perciò, ogni programma scritto in C standard garantisce la compilazione su
qualsiasi piattaforma che abbia una conforme implementazione del C.

Il motore, essendo nulla più che una collezione di algoritmi e strutture dati,
potrà quindi essere costruito in ANSI C per massimizzarne la portabilità pur
mantenendo alta l’efficienza, trattandosi di un linguaggio compilato.


Managed/Unmanaged Interoperability

Realizzando l’Interfaccia in XNA, si utilizzerà C#, un linguaggio Microsoft
facente parte del CLR di .NET. Si tratterà quindi di codice che gira in
macchina virtuale, ovvero managed.

Per contro, il Motore sarà scritto in ANSI C. Per la realizzazione in
Microsoft Windows verrà compilato come dll C++ ed esporrà delle funzioni
che potranno essere richiamate dall’Interfaccia tramite il sistema di
interoperabilità tra codice managed e unmanaged.

Si tratterà quindi di invocare codice unmanaged dal codice managed.
Vi sono alcune alternative per compiere questa operazione:

     •   Platform Invoke (detta anche P/Invoke) permette di richiamare
         funzioni scritte in qualsiasi linguaggio unmanaged a patto che la
         signature      sia    ridichiarata      in   codice     managed.

     •   COM interop permette di richiamare component COM da qualsiasi
         linguaggio                                         managed.

     •   C++ interop (anche detta It Just Works, IJW) è una caratteristica
         specifica del C++ che permette di utilizzare direttamente API COM e
         non solo. E’ più potente di COM interop ma richiede più cura.



58
Su MSDN si raccomanda di scegliere seguendo il flow-chart riportato in
figura 13.




Fig. 13 - Flow chart per la scelta della tecnologia per interoperabilità managed/unmanaged


La dll del motore non sarà certo una COM API, essendo scritta in C.
Con complex API si intende un’API che ha le signature dei metodi scritte in
modo difficile da dichiarare in codice managed. Essendo l’API ancora da
scrivere, faremo in modo che ciò non avvenga.
L’API una volta scritta rimarrà tale; o comunque il numero di funzioni sarà
sufficientemente piccolo (dell’ordine della decina) che anche se varierà non
sarà difficile propagare la variazione anche al codice managed.

Quindi la tecnologia per la comunicazione tra Interfaccia e Motore sarà
Platform Invoke.




                                                                                             59
Realizzazione del Motore

Il motore è stato realizzato in ANSI C, non essendovi necessità o particolari
benefici nel ricorrere all’OOP in un caso come questo.

Esso consiste di 5 file header “.h” e 8 file “.c”.


Header files


defs.h
defs.h, contenente la definizione delle costanti, dei tipi non primitivi
utilizzati e delle variabili globali. Qui sotto si riporta uno spezzone di codice
contente le strutture dati utilizzate per la memorizzazione delle mosse
generate.

moveBytes contiene l’indice della casella di arrivo e partenza di una mossa
(from e to), il pezzo a cui viene promosso nel caso di mossa di promozione
(promote), una serie di bit indicante il tipo di mossa (bits: i primi 3 bit di
questo char vengono usati per indicare se si tratta di una mossa di cattura,
e/o di un pedone, e/o di promozione).

typedef struct {
      int from;
      int to;
      int promote;
      char bits;
} moveBytes;

In union con un int in modo che si riesca a compararle rapidamente.
typedef union {
      moveBytes b;
      int u;
} moveUni;

gener_t associa la mossa ad un punteggio (score) utilizzato per il sorting
nell’implementazione dell’history heuristics.
typedef struct {
      moveUni m;
      int score;
} gener_t;


history_t è invece un elemento dello stack usato per mantenere la storia
della partita, in modo da poter ritornare indietro di una mossa.




60
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra
Tesi Specialistica - Bruno Tagliapietra

Weitere ähnliche Inhalte

Kürzlich hochgeladen

Limmi un successo che mette radici da un solo frutto: il limone di Sicilia
Limmi un successo che mette radici da un solo frutto: il limone di SiciliaLimmi un successo che mette radici da un solo frutto: il limone di Sicilia
Limmi un successo che mette radici da un solo frutto: il limone di SiciliaLimmi
 
Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...
Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...
Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...Associazione Digital Days
 
Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...
Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...
Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...Associazione Digital Days
 
Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...
Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...
Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...Associazione Digital Days
 
Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”
Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”
Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”Associazione Digital Days
 
Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...
Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...
Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...Associazione Digital Days
 
Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...
Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...
Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...Associazione Digital Days
 
Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...
Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...
Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...Associazione Digital Days
 
Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...
Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...
Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...Associazione Digital Days
 
Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...
Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...
Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...Associazione Digital Days
 
BioClima_Da gestori a custodi del territorio_Modulo 2
BioClima_Da gestori a custodi del territorio_Modulo 2BioClima_Da gestori a custodi del territorio_Modulo 2
BioClima_Da gestori a custodi del territorio_Modulo 2Etifor srl
 

Kürzlich hochgeladen (11)

Limmi un successo che mette radici da un solo frutto: il limone di Sicilia
Limmi un successo che mette radici da un solo frutto: il limone di SiciliaLimmi un successo che mette radici da un solo frutto: il limone di Sicilia
Limmi un successo che mette radici da un solo frutto: il limone di Sicilia
 
Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...
Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...
Edoardo Marrone, Direttore Commerciale di @Across- “Valorizzare il tesoro nas...
 
Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...
Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...
Enrico Busto, Strategic Technology Advisor fondatore di @Eticrea – “Reskillin...
 
Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...
Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...
Eleonora Sordella, Headhunter, Coach, Formatrice e Chiara Bonomi, Training De...
 
Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”
Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”
Luca Faccin, CEO @PerformancePPC – “Tool AI per migliorare i processi aziendali”
 
Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...
Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...
Pamela Serena Nerattini, Consulente LinkedIn e HR – “Ascoltare per crescere i...
 
Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...
Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...
Gianni Clocchiatti, Consulente per l’innovazione e la creatività d’impresa, F...
 
Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...
Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...
Mario Madafferi, Innovation Manager @Progesia SRL Società Benefit – “L’Integr...
 
Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...
Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...
Giulia Pascuzzi – Coach & Business Leader – Parlare di diversità è davvero in...
 
Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...
Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...
Simone Bonamin, Partner di @Argo Business Solutions S.r.l. – “Online Brand Pr...
 
BioClima_Da gestori a custodi del territorio_Modulo 2
BioClima_Da gestori a custodi del territorio_Modulo 2BioClima_Da gestori a custodi del territorio_Modulo 2
BioClima_Da gestori a custodi del territorio_Modulo 2
 

Empfohlen

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Empfohlen (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Tesi Specialistica - Bruno Tagliapietra

  • 1. Università degli Studi di Trieste Facoltà di Ingegneria Tesi di Laurea Specialistica in Ingegneria Informatica PROGETTAZIONE E REALIZZAZIONE DI UNA APPLICAZIONE PER IL GIOCO DEGLI "SCACCHI 3D" LAUREANDO: RELATORE: Chiar.mo Prof. Bruno TAGLIAPIETRA Maurizio FERMEGLIA Anno Accademico 2008/09 200
  • 2. 2
  • 3. Indice Introduzione 5 Motivazione 5 Analisi 7 Descrizione del gioco preso in esame 7 Raumschach 7 Pezzi 9 Scopo del gioco e relativo regolamento 16 Notazione delle mosse 16 Requisiti funzionali 17 Requisiti non funzionali 18 Progettazione 19 Considerazioni preliminari 19 Architettura del sistema 20 Interfaccia – Motore VS Model – View - Controller 20 Progettazione del Motore 21 Rappresentazione della scacchiera e generazione mosse 23 Tecniche di ricerca 32 Valutazione di una posizione 41 Progettazione dell’Interfaccia 50 Scacchiera Tridimensionale 50 Comunicazione Interfaccia-Motore 56 Realizzazione 57 Tecnologie utilizzate 57 Realizzazione del Motore 60 Header files 60 C Files 62 Realizzazione dei modelli 3D 75 Realizzazione dell’Interfaccia 77 Premessa: XNA Application Model 77 Librerie di terze parti 79 Gameplay 81 3
  • 4. Videocamere Virtuali 81 Gestione dei modelli 90 Logiche di gioco 92 Conclusioni 93 Raggiungimento degli obiettivi 93 Sviluppi futuri 93 Bibliografia 94 Letteratura 94 Web 94 Ringraziamenti 96 Appendice 97 Fast, Minimum Storage Ray/Triangle Intersection 97 4
  • 5. Introduzione Motivazione Il gioco degli scacchi è considerato un problema molto interessante dal punto di vista della sua risoluzione e gioco da parte di un elaboratore: nel corso degli anni, fin dagli albori dell’informatica, gli appassionati e i professionisti di scacchi e di computer hanno perfezionato algoritmi sempre più sofisticati, fino a riuscire recentemente a far vincere il computer contro i più grandi maestri. Vale la pena ricordare che gli scacchi, a differenza ad esempio della dama, sono ancora un problema aperto: cioè, mentre nella dama ormai si conosce la mossa “migliore” in ogni situazione, dall’inizio della partita fino alla fine, Vi sono competizioni mondiali che consistono in un gran numero di partite fra i cosiddetti “motori” per il gioco degli scacchi, ovvero software molto perfezionati e specifici il cui unico obiettivo è vincere una partita di scacchi. Alcuni fra i motori migliori sono stati poi inseriti nei programmi di scacchi commerciali, il più famoso di tutti è sicuramente “Chessmaster 10”: presenta innumerevoli funzionalità quali interfaccia grafica user friendly, possibilità di selezionare le abilità dell’avversario computerizzato (fino ad arrivare ad un’abilità stimata con punteggio FIDE 2966), un breve corso di scacchi integrato... Sarebbe stato quindi presuntuoso e difficilmente fattibile per un singolo scrivere qualcosa che sia competitivo sia dal punto di vista del motore, cioè dell’intelligenza del programma, sia dal punto di vista delle funzionalità offerte all’utente, rimanendo nel campo degli scacchi classici. Un’alternativa sarebbe stata inventare un gioco del tutto nuovo. Tuttavia spesso i giochi complessi difficilmente risultano equilibrati, interessanti e umanamente giocabili alla prima versione. Per questi motivi si è deciso di “riesumare” e computerizzare una fra le versioni più interessanti di questo gioco, che sono indubbiamente quelle che si svolgono su una scacchiera tridimensionale. Le tre principali alternative erano: Raumschach (in tedesco “scacchi nello spazio”), inventata nei primi del ‘900, Asimov Chess (versione ideata da Isaac Asimov in uno dei suoi racconti, ma mai sufficientemente giocata), Star Trek 3D Chess (versione giocata nell’omonima saga). 5
  • 6. Si sono scelti i Raumschach fra i tre perché essi sono stati indubbiamente i più giocati e i più sperimentati; inoltre non esiste alcuna implementazione sotto forma di videogame che sia fedele all’originale di questo gioco, ideato e giocato in alcuni club tedeschi nel periodo tra la prima e la seconda guerra mondiale. 6
  • 7. Analisi Descrizione del gioco preso in esame La versione scelta è, fra le versioni tridimensionali esistenti del gioco degli scacchi, la più documentata e la più giocata in assoluto. Le versioni tridimensionali del gioco degli scacchi, abbreviabili in 3D chess, fanno parte delle innumerevoli varianti di questo famosissimo gioco. Le più antiche risalgono alla fine del 1800. Una delle più famose versioni tridimensionali degli scacchi è appunto quella analizzata in questo documento. Essa è detta Raumschach, ovvero “scacchi spaziali” in lingua tedesca. Inventata nel 1907 da Ferdinand Maack, è stata giocata prevalentemente in Germania. Nel 1919 Maack fondò anche un club di Raumschach ad Amburgo, rimasto attivo fino all’inizio del secondo conflitto mondiale. Inizialmente giocata in un cubo 8x8x8, la versione del 1907 vide la luce su una scacchiera cubica con cinque caselle per ognuna delle tre dimensioni. Raumschach Come negli scacchi classici l’obiettivo è catturare il re avversario. Questo può avvenire solo tramite lo “scacco matto”, ossia una posizione in cui non vi è mossa in grado di impedire la cattura del re nella mossa immediatamente successiva. I contendenti muovono uno dei propri pezzi alla volta, a turno, alternandosi. I movimenti, descritti più avanti in dettaglio, sono simili a quelli degli scacchi classici. Ognuna delle 125 caselle è identificata da tre coordinate: “livello” di appartenenza, “riga” e “colonna”, in quest’ordine. • Livelli: identificati dalle lettere Fig. 1 Schema della scacchiera maiuscole A,B,C,D,E • Righe: identificate dalle cifre 1,2,3,4,5 • Colonne: identificate dalle lettere minuscole a,b,c,d,e Seguendo la disposizione di fig.2 si inizia, per convenzione, la numerazione delle caselle da quella posta sul livello più basso, sulla colonna più a sinistra e sulla riga più vicina al giocatore con i pezzi di colore bianco. Perciò, nella casella Aa 1 vi sarà all’inizio partita una torre “bianca”, e in Ba1 nel medesimo istante un alfiere “bianco”, con il re posto nel piano più basso. Sempre da fig.2 è facile ed importante notare che la disposizione dei pezzi 7
  • 8. neri risulta speculare a quella dei pezzi bianchi, quindi con il re posto sul posto livello più alto. Quindi in Ee5 vi sarà una torre “nera” e in Ed5 un unicorno “nero”. Fig. 2 - La disposizione iniziale della scacchiera, dal piano più alto (E) al piano più basso (A) 8
  • 9. Pezzi I pezzi sono, come negli scacchi classici, Re, Regina, Torri, Alfieri, Cavalli e scacchi Pedoni, con l’aggiunta degli Unicorni. Ogni giocatore ha a disposizione 10 pedoni, 2 torri, 2 cavalli, 2 alfieri, 2 unicorni, 1 regina e naturalmente 1 re. Come negli scacchi classici su ciascuna casella può stazionare un solo pezzo può e l’unico in grado di “saltare”, cioè di non subire l’ostruzione degli altri pezzi presenti sulla scacchiera, è il cavallo. Qualsiasi pezzo di un certo colore può occupare una casella contenente un pezzo avversario, catturando in questoi modo quest’ultimo, il quale verrà rimosso dalla scacchiera. Pedone: come negli scacchi classici il suoi movimenti cambiano a seconda che esso catturi o meno un pezzo. Senza cattura esso deve muovere di un passo di torre verso il campo avversario; se “bianco”, quindi, può muovere verso l’alto o verso avanti rispetto al giocatore che lo controlla. In caso di cattura a questo movimento dovrà essere aggiunto un cambio di colonna verso destra o verso sinistra, risultante in un passo d’alfiere sullo ste stesso piano o sul piano immediatamente successivo nel viaggio verso il campo avversario. A differenza degli scacchi classici non c’è differenza di possibilità di movimento fra la prima mossa e le successive. Non c’è mossa en-passant. Lettera identificativa: P 9
  • 10. Torre: può muovere in linea retta di quante caselle si vuole in un solo turno. Può muoversi in sei versi; è utile immaginare che essa per muoversi esca da una delle sei facce del cubo di partenza. Es: una torre posta al centro di una scacchiera vuota, quindi in Cc3, si può : vuota, muovere in o Cc1, Cc2,Cc4, Cc5 (Varia la riga) o Ca3, Cb3, Cd3, Ce3 (Varia la colonna) o Ac3, Bc3, Dc3, Ec3 (Varia il livello) Lettera identificativa: R (dall’inglese “rook”) 10
  • 11. Alfiere: anch’esso muove in linea retta per quante caselle si vuole in un solo caselle turno. A differenza della torre però esso esce dagli spigoli del cubo, perciò può muoversi in otto versi. Es: Un alfiere posizionato nella casella Dc4 in una scacchiera vuota può : muoversi in • Da2, Db3, Dd5, De2, Dd3, Db5 (nello ste stesso livello, come negli scacchi classici) • Eb4, Ed4, Cb4, cd4, Ba4, Be4 (nel piano posto frontalmente al giocatore) • Ec3, Ec5, Cc3, Cc5, Bc2, Ac1 (nel piano posto lateralmente al giocatore) Lettera identificativa: B (dall’ingelse “bishop”) 11
  • 12. Unicorno: anch’esso in linea retta per quante caselle si vuole in un solo nch’esso turno, ma uscendo dai vertici del cubo. Perciò si muove in quattro versi. Es: un unicorno posizionato al centro di una scacchiera vuota (Cc3) può : essere posto in: Dd4, Ee5, Bb2, Aa1, Db2, Ea1, Bd4, Ae5, Ee1, Dd2, Bb4, Aa5, Ea5, Db4, Bd2, , Ae1 Lettera identificativa: U 12
  • 13. Regina: essa può muovere sia come una torre, sia come un alfiere, sia come un unicorno. Lettera identificativa: Q (dall’inglese “queen”) 13
  • 14. Re: esso si sposta come la regina però di un solo passo per turno. Ovvero, la regina casella di arrivo deve essere adiacente alla casella di partenza cioè avere in comune con essa una faccia, uno spigolo o alla peggio un solo vertice. In altre parole un re può muovere in ciascuna delle otto caselle più vicine alla caselle casella di partenza. A differenza degli scacchi classici non vi sono manovre di arrocco. Lettera identificativa: K (dall’inglese “king”) 14
  • 15. Cavallo: E’ l’unico pezzo in grado di saltare oltre gli altri pezzi. Si deve muovere di un solo passo di torre più un solo passo di alfiere. In altre olo parole, date le coordinate della casella di partenza, una di esse deve restare invariata, una deve variare di 1 e la terza deve variare di 2. Es: in questi esempi indichiamo il variare delle coordinate racchiudendo tra : coordinate parentesi l’entità della variazione di livello, riga e colonna, in quest’ordine. Perciò ad esempio la tripla (0,1,2) indicherà un insieme di caselle poste contemporaneamente nello stesso livello di quella di partenza, su righe distanti 1 e colonne distanti 2. i Un cavallo posto al centro di una scacchiera, nella casella Cc3, può essere posto in ciascuna delle 24 caselle sottostanti, purché libere da pezzi dello stesso colore: Cb1, Cb5, Cd1, Cd5 (0,1,2) --------- Ca2, Ca4, Ce2, Ce4 (0,2,1) Bc1, Bc5, Dc1, Dc5 (1,0,2) --------- Ba3, Be3, Da3, De3 (1,2,0) Ab3, Eb3, Ad3, Ed3 (2,1,0) --------- Ac2, Ec4, Ac4, Ac2 (2,0,1) Lettera identificativa: N (dall’inglese “knight”) 15
  • 16. Scopo del gioco e relativo regolamento Come negli scacchi classici, lo scopo del gioco consiste nel dare "scacco matto" (dal persiano Shah Màt = il re è morto) al re avversario; si ha "scacco matto" quando il Re, trovandosi sotto la minaccia diretta dei pezzi avversari, non ha la possibilità di sottrarsi ad essa (cioè sarebbe sicuramente catturato alla mossa successiva, se non si trattasse del Re). Lo "scacco" invece è l'attacco (evitabile) che un pezzo avversario porta al Re. L'avversario non può eseguire alcuna mossa che metta o lasci il proprio re sotto "scacco". La partita può terminare anche per abbandono da parte di un contendente, ovviamente con la vittoria dell'altro. Il gioco termina obbligatoriamente in parità (patta) nei seguenti casi: 1. se restano sulla scacchiera soltanto i due re; 2. se il giocatore che ha il tratto non può muovere alcun pezzo, ma il suo re non è sotto scacco (stallo). 3. se per cinquanta mosse consecutive (cinquanta mosse per ciascun giocatore) non viene catturato alcun pezzo e non viene mosso alcun pedone. Notazione delle mosse Durante il corso della storia sono state ideate innumerevoli notazioni per gli scacchi classici. Senza dubbio la più utilizzata, per le sue caratteristiche di intuitività e brevità, è la cosiddetta “notazione algebrica”, descritta nel regolamento internazionale degli scacchi. Risulterebbe però problematico, e con tutta probabilità controproducente: infatti in essa si preferisce, per identificare una mossa, annotare la lettera iniziale del nome del pezzo che la esegue seguita dalle coordinate della casella di arrivo. Es: Cf3 (cavallo mosso in f3, non si sa da quale punto di partenza). E’ evidente che tale notazione necessita di complesse regole per evitare equivoci nel caso in cui, ad esempio, i cavalli che possono muovere in f3 siano più di uno. Per questo motivo, la notazione adottata nel nostro caso consisterà in: casella di partenza – casella di arrivo – iniziale di eventuale pezzo “di promozione” es: Aa1Ca1 (il pezzo presente in Aa1 va in Ca1), Ec4Ec5Q (il pezzo, pedone, presente in Ec4 va Ec5 e viene promosso in regina); 16
  • 17. Requisiti funzionali Di seguito sono riportati i requisiti funzionali raggruppati in macrofunzionalità. Ciascuna macrofunzionalità è raggiunta nel momento in cui vengono soddisfatti tutti i requisiti in esso contenuti. Requisiti giocabilità 2 giocatori su stessa postazione • Il software deve presentare una scacchiera cubica 5x5x5, conforme al regolamento indicato precedentemente. Detta scacchiera deve essere inizialmente configurata come da Fig.2 e contenere tutti e soli i pezzi indicati in Fig.2. • Si vuole inoltre che i movimenti consentiti sui pezzi siano conformi al regolamento precedentemente indicato. • Essendo la visualizzazione di una scacchiera 3D ostica all’essere umano, si vuole che vi siano più modalità di visualizzazione della stessa (lontano-vicino). • Per selezionare il pezzo da muovere e conseguentemente la casella in cui muoverlo si vuole poter usare il mouse: un primo click sul pezzo lo deve evidenziare, un secondo click fuori dal pezzo deve: o Spostare il pezzo se il click è stato effettuato su una casella dove il pezzo stesso possa muovere. o Selezionare un pezzo diverso se il click è stato effettuato su un altro pezzo selezionabile. o Deselezionare il pezzo selezionato altrimenti. 17
  • 18. Requisiti giocabilità giocatore umano vs giocatore computerizzato • Si vuole che vi sia la possibilità di giocare contro un avversario computerizzato. • Si vuole che l’abilità di detto avversario sia regolabile. Per la precisione: o Deve essere possibile regolare il tempo massimo concesso per eseguire una mossa. o Deve essere possibile fissare la profondità di analisi massima del software. Requisiti giocabilità 2 giocatori umani su postazioni diverse • Si vuole che vi sia possibilità di giocare via internet con un avversario umano. Requisiti non funzionali • Portabilità: si vuole che il gioco sia cross platform il più possibile. 18
  • 19. Progettazione Considerazioni preliminari Dallo studio dei requisiti emerge come prima peculiarità il fatto che si tratti di arrivare alla realizzazione di un videogame. Già questa semplice particolarità presenta non poche implicazioni progettuali: Restringendo il campo e riassumendo, dal punto di vista informatico si tratta di un gioco da tavolo (board videogame) dotato di “intelligenza artificiale” (AI), che deve quindi essere in grado di tener testa ad un giocatore umano. Si delina quindi una prima suddivisione in “motore”, ossia la parte pensante del gioco, e “interfaccia utente”, cioè tutto ciò che è percepito sensorialmente dal giocatore umano. Dovendo essere intelligente, il motore deve avere come caratteristiche peculiari rapidità di calcolo, efficienza, ed un certo grado di “regolabilità della difficoltà”. L’interfaccia, per contro, dovrà soddisfare i requisiti richiesti; inoltre, non avendo particolari requisiti di efficienza, sarà facile progettarla in modo che sia il più manutenibile possibile. Altro punto fondamentale, si tratta di un software che fa uso di grafica 3D. Sarà quindi necessaria la scelta di tecnologie adatte a coprire le necessità emerse da queste prime considerazioni iniziali. In particolare si avrà bisogno di: • Un game engine con supporto 3D (OpenGL, Direct3D o entrambi). E’ un software che serve a sviluppare video game. Le funzionalità principali fornite da un game engine comprendono un renderer per grafica 2D e/o 3D, animazioni, networking, gestione della memoria, threading. In più quello da noi scelto dovrà facilitare la portabilità, come da requisito. • Un software di 3D modeling per la realizzazione dei modelli (i pezzi, la scacchiera). 19
  • 20. Architettura del sistema Nella primissima fase della definizione architetturale è opportuno mantenersi ad alti livelli di astrazione, scendendo nei dettagli il meno possibile. Via via che l’architettura prende forma, la definizione delle singole parti sarà sempre più dettagliata. Interfaccia – Motore VS Model – View - Controller In accordo alle considerazioni preliminari, in prima approssimazione viene molto naturale suddividere il progetto in due macroparti, come segue: • Interfaccia Utente Si occupa di raccogliere input dall’utente (le mosse che egli intende dall’utente fare) e di visualizzare a video la scacchiera. • Motore In esso sono implementate le regole del gioco, lo stato della partita e gioco, la logica di gioco computerizzato. In questo modo, ad ogni input dell’utente l’Interfaccia Utent dovrà: Utente 1. Compiere un’operazione di traduzione dell’input. 2. Passare al Motore la suddetta traduzione. 3. Attendere risposta del Motore. 4. Aggiornare la visualizzazione basandosi sullo stato del Motore. Ad ogni input ricevuto, il Motore dovrà: 1. Porre a confronto l’input ricevuto con le regole. 2. Se l’input ricevuto corrisponde ad una mossa permessa, modificare lo stato e generare un messaggio di conferma. Altrimenti, generare un messaggio di errore. 3. Rispondere con il messaggio generato all’Intefaccia Utente 20 Fig. 3 Model - View - Controller
  • 21. L’architettura così descritta richiama il pattern archietturale Model View Controller. Il comportamento del sistema viene normalmente così illustrato: 1. L’utente interagisce in qualche modo con l’interfaccia (View) 2. Il Controller reagisce all’input, ad esempio tramite una callback. 3. Il Controller propaga sul Modello il risultato dell’azione dell’utente; solitamente modifica lo stato del Modello. 4. Una View utilizza il modello indirettamente per generare un’interfaccia utente appropriata, o per modificare quella già esistente. Il Controller e il Modello non “sanno” che la View esiste. Nel nostro caso Model e Controller farebbero entrambi parte del Motore. Questa è infatti l’architettura più conveniente; tutto lo stato è contenuto nel Motore, che viene interrogato dall’Interfaccia ogniqualvolta ve ne sia bisogno. Progettazione del Motore Per essere in grado di assolvere alla sua funzione in base ai requisiti descritti, il Motore di 3D Chess deve innanzitutto possedere un sistema di strutture dati atte a descrivere lo stato della partita in corso. Lo stato sarà formato da: • Stato della scacchiera, ovvero stato di ciascuna delle 125 caselle. Ciascuna di esse può essere vuota oppure contenere uno dei 7 tipi di pezzi disponibili, che possono essere a loro volta “bianchi” o “neri. Perciò ogni casella ha 7 ∗ 2 + 1 = 15 stati possibili. • Numero di mosse consecutive dall’ultima cattura di pezzo o mossa di pedone (essenziale per soddisfare la regola di partita patta in caso di 50 mosse consecutive senza catture o mosse di pedoni). • Giocatore che ha il tratto (cioè: tocca muovere al bianco o al nero?) • Inoltre, volendo dare la possibilità di “takeback”, ovvero di poter annullare un certo numero di mosse, sarebbe opportuno mantenere uno stack delle mosse fatte fino a quel momento. In alternativa, per semplificare il calcolo, si potrebbero memorizzare direttamente le posizioni della scacchiera. 21
  • 22. A questo punto, basandosi sullo stato così descritto, il Motore deve essere in grado di: • Esprimere una valutazione quantitativa sulla posizione • Per un certo numero di “livelli”, si esso n: o Generare tutte le mosse possibili o Valutare tutte le posizioni o Per ciascuna posizione, generare tutte le mosse possibili o Fermarsi quando ha raggiunto il tempo limite In questo modo si sarà in grado di fissare il “livello” di difficoltà sia basandosi sulla massima profondità di esplorazione dell’albero delle mosse, sia fissando un tempo limite massimo oltre il quale il Motore dovrà selezionare la mossa ritenuta fino a quel momento la migliore. Naturalmente, tutto questo deve essere fatto nel modo più efficiente possibile, in quanto questo è il requisito fondamentale che deve avere il nostro Motore. Da queste considerazioni iniziali si può facilmente concludere che tre sono i problemi fondamentali da risolvere: • Rappresentazione della scacchiera: quali strutture dati utilizzare per rappresentare una posizione? • Tecniche di ricerca: come identificare le mosse possibili e selezionare quelle che sembrano essere più “promettenti” in modo che vengano esplorate per prime? • Valutazione di una posizione: come valutare quantitativamente una posizione? Descriviamo ora i punti presi in considerazione nella progettazione della soluzione a questi tre principali problemi. 22
  • 23. Rappresentazione della scacchiera e generazione mosse Nei motori per il gioco degli scacchi classici vengono utilizzati da molti anni delle strutture dati ormai considerate standard per la rappresentazione di una scacchiera. E’ nostro intento adottare la più consona ai nostri scopi, fornendo dapprima una descrizione delle possibili alternative. Alternative progettuali 1. Lista di pezzi Era utilizzata dai primi programmi di scacchi, i quali avevano a disposizione pochissima memoria. Perciò anziché riferirsi prima alla cella e da essa ottenere il pezzo, mantenevano una lista dei pezzi in gioco e relative coordinate. E’ utilizzata ancora oggi in congiunzione ad altre tecniche nel caso serva scoprire rapidamente la posizione di un certo pezzo. 2. Array based Una fra le alternative più intuitive, ovvero creare un array di tante caselle quante sono quelle della scacchiera che trattiamo. Ciascun elemento dell’array identificherà il contenuto della casella: ad esempio assegnando un valore convenzionale a ciascuna delle 15 possibili combinazioni (vuota, uno dei 7 tipi di pezzi bianchi o uno dei 7 tipi di pezzi neri). Diventa lento durante la generazione delle mosse in quanto per ogni mossa generata bisogna controllare se essa è dentro o fuori dalla scacchiera, rallentando in questo modo il processo di generazione. 3. Codifica Huffman Ispirato alla nota codifica di Huffman, le posizioni vengono descritte con configurazioni di bit di lunghezza inversamente proporzionale alla probabilità che esse compaiono. Ecco un esempio che farebbe al caso nostro: Casella vuota = 0 Pedone = 10c Unicorno = 1100c Alfiere = 1101c Cavallo = 1110c Torre = 1111c Regina = 11110c Re = 11111c dove ‘c’ rappresenta il colore (ad esempio 1 per bianco e 0 per nero). 23
  • 24. Servono ancora 1 bit per identificare chi ha il tratto e 7 bit per la regola delle 50 mosse. Questa strategia risulta molto onerosa dal punto di vista della complessità di calcolo, mentre è conveniente per le ridotte dimensioni in memoria. 4. Metodo 0x88: Questo metodo funziona negli scacchi classici perché le 64 caselle sono in numero una potenza di 2, come pure le due dimensioni 8x8. Non può essere utilizzato nel nostro caso (la scacchiera è composta da 125 caselle, con tre dimensioni 5x5x5, e non vi sono potenze di 2 fra questi numeri), ma lo citiamo, senza spiegarlo, per completezza. 5. Bitboard: Anche questo metodo non funziona per noi. Consiste nello sfruttare la parallelizzabilità del calcolo in caso di processori a 64 bit quando le caselle sono 64, come negli scacchi normali. Anche questo è citato solo per completezza 6. Notazione Forsyth–Edwards (FEN) Basata su un sistema ideato dal giornalista scozzese David Forsyth, è stata estesa da Steven J. Edwards per utilizzi informatici. Un “record” FEN definisce una particolare posizione di gioco; si tratta di una stringa di caratteri ASCII tutti sulla stessa riga, contenente sei campi, separati da uno spazio. I campi sono: (NB: la sguente descrizione si riferisce al regolamento degli scacchi classici. E’ qui riportata per confronto con la variante FEN elaborata per il caso nostro e descritta in seguito) a. Posizionamento dei pezzi (dalla prospettiva del bianco) Ciascuna delle 8 righe è descritta come segue, ed è separata dalle altre utilizzando il carattere “/”. Seguendo la notazione algebrica standard (stessa iniziale dei pezzi descritta in questo documento nella parte di analisi), i pezzi bianchi sono designati usando lettere maiuscole (“PNBRQK”) mentre quelli neri con lettere minuscole (“pnbrqk”). Le caselle vuote consecutive vengono raggruppate e indicate con cifre da 1 a 8. b. Colore di chi ha il tratto “w” per bianco (white), “b” per nero (black). 24
  • 25. c. Arrocco Se non si può più arroccare si segnerà “-“. Altrimenti il vocabolario è il seguente: “K” (il bianco può arroccare dal lato di re), “Q” (il bianco può arroccare dal lato di regina), “k” (il nero può arroccare dal lato di re), “q” (il nero può arroccare dal lato di regina). d. En Passant Se un pedone può essere mangiato “en passant” si segneranno le coordinate della casella sulla quale finirà un eventuale pedone avversario che effettuerà la mangiata. Altrimenti si segna “-“. e. Halfmove clock Così si definisce il numero di mezze mosse passate da quando è stato mosso un pedone o mangiato un pezzo. Se raggiunge 100 la partita è pari. f. Fullmove number: Il numero di mosse complete eseguite fino al momento della scrittura della posizione. Comincia da 1 e viene incrementato ogniqualvolta muove il nero. Esempio di record FEN per scacchi classici (appena descritto) nella posizione iniziale: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 Ispirandoci a questa notazione possiamo derivarne con poco sforzo una rappresentazione appropriata ed intuitiva per il caso preso in esame da questa tesi. Notazione Forsyth–Edwards-Raumschach (FENR) Non contemplando il regolamento del gioco da noi preso in esame la presa “en passant” né tantomeno alcun tipo di arrocco, i campi che compongono un record FENR saranno soltanto 4, separati da spazi. Gli ultimi 3, ovvero “Colore di chi ha il tratto”, “Halfmove clock” e “Fullmove number”, rimarranno fedeli all’originale. Il primo, riguardante la descrizione del posizionamento dei pezzi, sarà annotato come segue. Come nell’originale la scacchiera verrà rappresentata dal punto di vista del bianco. I piani saranno scritti dal più alto al più basso. La rappresentazione delle caselle vuote e le lettere rappresentanti i pezzi seguiranno le regole dell’originale, con l’aggiunta della “u” e “U” per gli unicorni rispettivamente neri e bianchi. 25
  • 26. Come nell’originale le righe saranno separate da “/”. I piani, invece, saranno separati da “-“. Esempio di record FENR per 3D Chess nella posizione iniziale: rnknr/ppppp/5/5/5-buqbu/ppppp/5/5/5-5/5/5/5/5- 5/5/5/PPPPP/BUQBU-5/5/5/PPPPP/RNKNR w 0 0 (cfr. con fig. 2). 7. Mailbox Array E’ una variante della struttura dati Array Based (descritta al punto 2), mantiene i suoi vantaggi ed abbatte la difficoltà di determinare, durante la generazione delle mosse, se la mossa generata finisca dentro o fuori dalla scacchiera. Descriviamo dapprima il metodo utilizzato negli scacchi classici, più semplice in quanto riferito ad una scacchiera bidimensionale. Per rappresentare la scacchiera si utilizzano 2 array monodimensionali di interi. Uno formato da 8 ∗ 8 = 64 caselle, denominato “addresser”. L’altro formato da 12 ∗ 12 = 144 caselle, denominato “mailbox”. Mailbox sarà così composto: -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 1 2 3 4 5 6 7 -1 -1 -1 -1 8 9 10 11 12 13 14 15 -1 -1 -1 -1 16 17 18 19 20 21 22 23 -1 -1 -1 -1 24 25 26 27 28 29 30 31 -1 -1 -1 -1 32 33 34 35 36 37 38 39 -1 -1 -1 -1 40 41 42 43 44 45 46 47 -1 -1 -1 -1 48 49 50 51 52 53 54 55 -1 -1 -1 -1 56 57 58 59 60 61 62 63 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 26
  • 27. Addresser sarà invece composto come segue: 26 27 28 29 30 31 32 33 38 39 40 41 42 43 44 45 50 51 52 53 54 55 56 57 62 63 64 65 66 67 68 69 74 75 76 77 78 79 80 81 86 87 88 89 90 91 92 93 98 99 100 101 102 103 104 105 110 111 112 113 114 115 116 117 Ovvero addresser conterrà tutti e soli, nell’ordine, gli indici delle caselle di Mailbox che non contengono −1. A questi due array si aggiunge la scacchiera Array Based, sia esso ℎ , costituita da un array monodimensionale di interi composto da 64 caselle. I pezzi vengono rappresentati in esso con valori convenzionali. Esempio: 0: casella vuota 1: pedone bianco , -1 pedone nero 2: unicorno bianco , -2 pedone nero 3: cavallo bianco , -3 cavallo nero 4: alfiere bianco , -4 alfiere nero 5: torre bianca , -5 torre nera 6: regina bianca , -6 regina nera 7: re bianco , -7 re nero Ora, per ogni verso in cui ciascun pezzo può muoversi si definisce un offset. Aggiungendo questo offset all’indice della casella in cui esso si trova si otterrà la successiva casella nella direzione di movimento di quell’offset. Ad esempio, per l’alfiere gli offset saranno 4, uno per ogni direzione, e saranno −13, −11, 11, 13. Infatti, un ipotetico alfiere in posizione 115, ovvero la posizione di partenza dell’alfiere di re bianco, potrebbe muoversi ad esempio nelle caselle 115 − 13 = 102 e 115 − 11 = 104 (prendendo come riferimento l’array Addresser). E infatti: mailbox[102] contiene 52 e mailbox[104] contiene 54 Infatti 52 e 54 sono le nuove coordinate dell’alfiere nella scacchiera Array Based. 27
  • 28. Si vede subito che tentando di far muovere l’alfiere in questione fuori dalla scacchiera si ottiene −1. Infatti 115 + 11 = 126 e 115 + 13 = 128. E mailbox[126] contiene -1 e mailbox[128] contiene -1 Vediamo l’algoritmo in linguaggio naturale: con questo sistema, per ogni casella 0 ≤ ≤ 63 sarà necessario e sufficiente: • Se la casella non è vuota, fare riferimento agli offset del pezzo in essa contenuto. • Per ogni offset: 1. Sommare il primo offset ad [ ]. Sia l’intero così ottenuto. 2. Se [ ] contiene −1 fermarsi e passare all’offset successivo. Se sono finiti gli offset, passare alla casella successiva. 3. Altrimenti significa che il pezzo può muoversi in ℎ [ [ ]] , a meno che questa casella non sia occupata da un altro pezzo dello stesso colore o che a seguito di questa mossa il re non venga messo sotto scacco. 4. Se il pezzo può muoversi di più caselle in una direzione (alfiere, torre o regina) ripetere il punto 1. Altrimenti passare all’offset successivo. Se sono finiti gli offset, passare alla casella successiva. NOTA: Le colonne e le righe contenenti −1 sono due per ogni lato dell’array. Ne basterebbero soltanto una per lato se non fosse per il cavallo, il quale nel caso in cui si trovasse sul bordo di uno dei lati potrebbe essere spinto appunto di due colonne, o righe, fuori dalla scacchiera da uno dei suoi offset. A questo punto si può immaginare un’implementazione di questo algoritmo in C. Avendo già descritto l’algoritmo in linguaggio naturale sembra superfluo utilizzare lo pseudocodice, e più appropriato il C, anche per sottolineare la semplicità di calcolo ottenuta con la struttura dati “Mailbox Array” Siano: 28
  • 29. “mailbox” l’array da 144 caselle. • “addresser” l’array da 64 caselle. • “chessboard” la scacchiera Array Based (supponiamo che il valore convenzionale per “casella vuota” sia zero), quindi da 64 caselle. • “color” un array da 64 caselle contenente −1, 1 o 0 a seconda che una casella sia occupata da un pezzo nero, bianco o vuota, rispettivamente. • “offset” un array bidimensionale contenente in ogni colonna tutti gli offset per muovere ciascun pezzo (zero per i pezzi che hanno meno direzioni). • “slide” un array di boolean che contiene “vero” nelle celle destinate ad alfiere, torre e regina, “falso” nelle altre. L’algoritmo necessario a determinare tutte le caselle in cui il pezzo contenuto nella casella “i” (da zero a 64) può essere mosso è il seguente: // *** ALGORITMO DI GENERAZIONE MOSSE BOOL is_moveable = FALSE; int n; for (j = 0; j < offsets[piece[i]]; ++j) //il tipo di pezzo contenuto in piece[i] //ha offsets[piece[i]] offset diversi for (n = i;;) { //inizializzazione e ciclo infinito //”n” è la casella da esaminare n = mailbox[addresser[n] + offset[piece[i]][j]]; //utilizzando le schematizzazioni di mailbox e //addresser sopra indicate è facile capire che //con questa operazione “n” diventa -1 nel caso //in cui il movimento usando l’offset //individuato nel modo descritto porti fuori //dalla scacchiera if (n == -1) break; //se siamo usciti dalla scacchiera ci fermiamo //e passiamo all’offset successivo if (color[n] != 0) { //se la casella non è vuota... if (color[n] == not_my_side) is_moveable = TRUE; //se è occupata da un pezzo avversario allora 29
  • 30. //è possibile occuparla (catturando il pezzo) break; } is_moveable = TRUE; if (!slide[piece[i]]) break; //se si tratta di alfiere, torre o regina //esaminiamo un ulteriore spostamento nella //direzione dell’offset preso in esame } E’ chiaro che questo algoritmo risolve il problema della generazione delle mosse Passando tutte le 64 caselle in questo modo si ha rapidamente la situazione della scacchiera in una determinata posizione. A questo punto si tratta di portare questo metodo alla nostra situazione di scacchiera cubica. A tal proposito, sarà sufficiente: 1. Modificare l’array “Mailbox” in un array con 9x9x9 = 729 caselle, in modo da avere in ogni direzione sufficiente spazio per eventuali pezzi “caduti” fuori dalla scacchiera. Per ogni dimensione, infatti, avremo le 5 caselle utili più 2 caselle per lato (necessarie a mantenere i movimenti dei cavalli) 2. Modificare l’array “addresser” trasformandolo in un array di 125 caselle. Anch’esse, come nell’esempio degli scacchi classici, dovranno contenere gli indirizzi delle caselle di Mailbox che non contengono “-1”. 3. Costruire opportunamente l’array degli offset basandosi sulle dimensioni di “mailbox”. 4. Modificare gli array “chessboard” e “color” in array da 125 caselle. 5. Modificare l’array “slide” aggiungendo ad esso l’unicorno. L’algoritmo resterà invariato e il metodo sarà applicabile al caso nostro. 30
  • 31. Soluzione adottata La soluzione più adatta al caso nostro è sicuramente “Mailbox Array”, unendo essa la praticità della scacchiera “Array Based” senza però gli handicap di inefficienza di cui quest’ultima soffre. Anche la facilità di adattabilità della metodologia al nostro tipo di problema la rende la miglior candidata fra le proposte elencate. Esso però è difficilmente applicabile al caso del pedone: il movimento del pedone è condizionato in modo particolare dai pezzi avversari che lo circondano; esso infatti può muovere in diagonale soltanto se vi sono presenti pezzi avversari nelle caselle di destinazione (per un’accurata descrizione del movimento del pedone v. Analisi). Basterà utilizzare l’algoritmo di generazione precedentemente individuato solo per i pezzi che non siano pedoni, anteponendo ad esso un controllo condizionale. Per i pedoni sarà sufficiente controllare lo stato delle 6 caselle interessate dal movimento di ciascuno di loro, a seconda che sia nero o sia bianco, in modo da massimizzare l’efficienza pur scrivendo codice ridondante. In pseudocodice: foreach (casella c della scacchiera) { if (color[c] == colore di turno) { if (piece[c] == pedone) { if(color[c] == bianco) { //controllo dello stato delle 6 caselle //corrispondenti al movimento pedone bianco } else { //controllo dello stato delle 6 caselle //corrispondenti al movimento pedone nero } } else { //esegui algoritmo di generazione mosse } } } I dettagli sulla effettiva implementazione sono riportati nella sezione dedicata alla realizzazione. 31
  • 32. Tecniche di ricerca La ricerca di una mossa in un gioco a turni si risolve, in generale, sotto forma di ricerca in un albero. La prima idea che viene in mente è impostare una massima profondità, generare tutto l’albero fino a quella profondità e valutare le “foglie”, ovvero le posizioni definite terminali. E’ ovvio che in ogni caso la “visibilità” che l’algoritmo consente è limitata dalla profondità impostata. Questo fenomeno è chiamato “horizon effect”: il numero dei possibili stati, o posizioni, è molto grande e I computer possono cercare soltanto fra una piccola parte di essi. Perciò potrebbe capitare ad esempio che la mossa ritenuta migliore guardando soltanto 5 mosse più avanti risulti disastrosa, al punto di essere determinante per la perdita della partita (pensiamo ai famosi “sacrifici”: ne è un esempio lampante la famosa partita a scacchi detta “L’immortale”, nella quale il bianco vince pur essendo in schiacciante inferiorità numerica). Per questo motivo negli anni si è tentato di ottimizzare la ricerca della mossa migliore facendo in modo di selezionare i “rami” nei quali cercare con profondità maggiore o nei quali cercare prima. Perciò il problema della ricerca viene a sua volta scomposto in tre problemi: 1. Algoritmo di ricerca da utilizzare 2. Come ottenere una differenza efficace di profondità nella ricerca 3. In quale ordine effettuare la ricerca nei vari livelli dell’albero Algoritmo di ricerca Di seguito si elencano brevemente alcune fra le procedure di ricerca più utilizzate: • Minimax Così cita il teorema del minimax (dalla teoria dei giochi): per ogni gioco a somma zero con strategie finite che interessa due giocatori, esiste un valore V ed una strategia mista per ciascun giocatore tale che: a. Data la strategia del giocatore 2, il miglior risultato per il giocatore 1 è V. b. Data la strategia del giocatore 1, il miglior risultato per il giocatore 2 è –V. 32
  • 33. Senza entrare ne merito della teoria dei giochi, il cui nel approfondimento va al di là dello scopo di questa tesi, possiamo applicare questo teorema agli scacchi, ed anche ai nostri scacchi 3D. Infatti, dall’applicazione del teorema del minimax scaturisce l’applicazione l’algoritmo minimax. Esso è un algoritmo ricorsivo che serve minimax. generalmente a cercare la miglior mossa successiva in un gioco di n giocatori. Viene associate un valore a ciascuna posizione o stato del sta gioco; questo valore indica quanto buono è per un giocatore raggiungere q quella posizione. Il giocatore poi farà la mossa che massimizza il minimo valore della posizione risultante dalle possibili mosse successive dell’avversario. Min Fig. 4 Esempio di albero minimax con valori calcolati Il valore dei nodi viene valutato con una funzione euristica discussa e descritta più avanti. a Di seguito un’implementazione in pseudocodice dell’algortimo maximin. function minimax(node, depth, color) if ((node è un nodo terminale) or (depth == 0)) node terminale depth 0 return color * (valore euristico del nodo valore nodo) else α := - -∞ foreach child of node α := max(α, max(α,-minimax(child, depth-1), -color) color) return α Alla prima chiamata si setta il nodo di partenza, il colore che deve setta muovere e la profondità massima da raggiungere. 33
  • 34. Alfa-Beta pruning È un algortimo di ricerca il cui obiettivo è ridurre il numero di nodi da valutare rispetto all’algoritmo minimax: esso infatti smette di valutare una mossa quando è stata trovata almeno una possibilità in grado di provare che la mossa in questione è peggiore di una precedentemente esaminata. Questo farà sì che tale mossa non necessiti di ulteriore valutazione. E’ un’ottimizzazione che non cambia il risultato dell’algoritmo, quindi sicuramente da adottare. I benefici dell’alfa-beta pruning consistono nel fatto che utilizzandolo si riescono ad eliminare rami dell’albero di ricerca: in questo modo il tempo di ricerca può essere limitato ai rami “più promettenti”, permettendo così una più profonda ricerca. L’ottimizzazione riduce la profondità effettiva a poco più della metà rispetto al minimax se i nodi sono valutati in ordine ottimale o quasi ottimale. La dimostrazione di tale affermazione esula dagli obiettivi di questa tesi. L’algoritmo funziona mantenendo due valori, alfa e beta, che rappresentano il minimo punteggio ottenibile dal giocatore massimizzante e il massimo punteggio ottenibile dal giocatore minimizzante, rispettivamente. Alfa è inizialmente infinitamente negativo, beta infinitamente positivo. La loro distanza decresce al progredire della ricorsione. Nel momento in cui beta diventa minore di alfa significa che la posizione corrente non può essere il risultato del gioco ottimale di entrambi i giocatori, perciò diventa inutile esplorarla ulteriormente: è infatti inutile esplorare eventualità di gioco non ottimali nella speranza che esse si verifichino; questo, negli scacchi ma non solo, è uno dei più gravi e comuni errori dei giocatori principianti: sperare in un grossolano errore dell’avversario per assicurarsi la vittoria. 34
  • 35. Fig. 5 - Una illustrazione di alfa-beta pruning. Esplorare i sottoalberi in grigio non beta migliora la soluzione (nell'esplorazione da sinistra a destra). "min" e "max" rappresentano i turni del giocatore e dell'avversario rispettivamente. Pseudocodice dell’algori dell’algoritmo: function alfa alfabeta(node, depth, α, β)) //β rappresenta la miglior scelta della mossa preecedente (e quindi dell’altro giocatore) if ((node è un nodo terminale) or (depth = 0)) node depth == return (valore euristico del nodo) foreach child of node α := ma max(α, -alfabeta(child, depth-1, 1,-β,-α)) // uso della simmetria, -β diventa α if β≤α break // Beta cut-off return α //chiamata iniziale alfabeta(origin, depth, -infinity, +infinity) beta(origin, infinity, NOTA: Dalla fig. 5 si nota che se i sottorami di terzo livello, figli del nodo di i secondo livello marcato con valore 3, fossero scambiati di posto, uno di loro non verrebbe nemmeno analizzato perché verrebbe tagliato fuori dal pruning. Infatti la tecnica di pruning può venire ulteriormente migliorata p utilizzando metodi di ordinamento euristici per individuare parti di alberi che probabilmente potrebbero causare tagli se analizzate per prime. Idealmente, sarebbe necessario che fossero esaminate per prime le mos “migliori”. mosse Una delle tecniche più diffuse in questo senso, per la sua efficacia e basso costo di calcolo, è la cosiddetta killer heuristic. 35
  • 36. Killer Heuristic Si tratta di una tecnica di ordinamento di alberi in supporto all’algoritmo di alfa-beta pruning. Si tenta di produrre un cutoff sperando che una mossa che ha prodotto un cutoff in un altro ramo dell’albero alla stessa profondità produca un cutoff nella posizione in cui ci si trova al momento. Particolarmente efficace negli scacchi, dove spesso una mossa buona si rivela tale anche in posizioni di poco diverse. Perciò, esplorando la mossa killer prima di altre mosse spesso si riesce a creare un taglio, il cui beneficio sarà tanto maggiore quanto minore sarà la profondità in cui lo si esegue. History Heuristic Ulteriore evoluzione, di comprovata efficacia negli scacchi classici, è la cosiddetta history heuristic. Si costruisce una tabella di mosse, indicizzandole in qualche modo, ad esempio tramite il pezzo che muove e la casella di destinazione. Al verificarsi di un taglio l’elemento appropriato della tabella viene incrementato, ad esempio aggiungendo ad esso la profondità rimanente (quindi mosse meno profonde risulteranno avere punteggio più alto). In questo modo si tenderà a privilegiare, fra le mosse che hanno causato tagli, quelle avvenute in rami meno profondi dell’albero, quindi in grado di produrre tagli più cospicui. Qualunque sia l’algoritmo di ordinamento utilizzato, questo dovrà avvenire prima della ricerca sull’albero. • Quiescenza Questo metodo di ricerca si è provato funzionale sperimentalmente, e deriva da un’imitazione di comportamento umano: un giocatore infatti tende istintivamente ad analizzare più in profondità situazioni “agitate”, ovvero mosse attraverso le quali la posizione viene profondamente alterata; tipicamente, mosse di cattura. Il metodo consiste semplicemente nel dedicare maggiore profondità all’analisi di mosse di cattura. • Principal Variation + Iterative Deepening Il termine variazione si riferisce ad una specifica sequenza di mosse in un gioco a turni generico; spesso è utilizzato per identificare un ipotetico stato futuro del gioco. 36
  • 37. La variazione principale (principal variation) è quella particolare variazione che è la più vantaggiosa per il giocatore corrente, assumendo che il giocatore avversario risponda con le mosse per lui di volta in volta migliori. In altre parole essa è la “migliore” o “corretta” linea di gioco. Nel contesto qui preso in esame si indica con variazione principale la sequenza che il giocatore computerizzato crede essere la migliore; questo assunto non è garantito a causa dell’euristicità dell’algoritmo di valutazione e dell’horizon effect. Fig. 6 In blu la variazione principale di un ipotetico albero minimax Estendendo ancora il significato del termine, si può pensare di avere una variazione principale per ciascun grado di profondità dell’albero: ciascuna di esse sarebbe lunga quanto la profondità esaminata. Utilizzare un array bidimensionale quadrato (avente come “lato” la massima profondità che si decide di analizzare), unito ad uno schema iniziale di iterative deepening, ovvero un ciclo che richiama l’algoritmo di alfa-beta pruning con limite di profondità inizialmente pari a 1 e ad ogni iterazione incrementato fino ad un certo massimo, permette di mantenere la “storia” delle soluzioni trovate e l’evoluzione della soluzione migliore trovata verso l’ottimalità attraverso i vari passaggi all’interno dell’albero. 37
  • 38. Soluzione adottata Per la sua comprovata capacità di migliorare l’efficienza rispetto al Minimax, l’algoritmo che si utilizzerà sarà l’alfa-beta pruning, corredato da ricerca di quiescenza a profondità maggiore. Inoltre, si utilizzerà una tabella per l’implementazione dell’algoritmo “History Heuristics”. In questo modo si potrà efficientemente compiere ricerche in profondità nell’albero, potendo così supplire alle carenze (inevitabili) della funzione di valutazione della posizione. Si è desciso di utilizzare una ricerca a tre livelli. Riferendosi alla figura 7: una prima funzione, think, richiama l’algoritmo alfa-beta pruning, chiamato search, dando ad esso profondità limite inizialmente pari ad 1 e ad ogni iterazione incrementata di 1. Questo è necessario per soddisfare il requisito della limitazione temporale nella nostra ricerca: alfa-beta pruning è un algoritmo di tipo depth-first; questo significa che non si ha soluzione fino a che non è stato esplorato tutto l’albero alla massima profondità desiderata. Invece, impostando la profondità massima di volta in volta maggiore, ed utilizzando un array per contenere tutte le variazioni principali ottenute nelle varie iterazioni, l’algoritmo di ricerca potrà essere interrotto anche dopo brevissimo tempo: in caso di interruzione verrà utilizzata la miglior soluzione trovata fino a quel momento, indipendentemente da quanto in profondità si sia riusciti ad andare. Ad ogni iterazione viene chiamata la search, che implementa alfa-beta pruning. Essa genera tutte le mosse possibili nella posizione corrente e, iterando sull’elenco che le contiene, ne esegue la i-esima, richiama sé stessa (invertendo alfa e beta perché il punteggio del giocatore nero è opposto a quello del giocatore bianco) riducendo la profondità rimanente (depth), annulla la mossa i-esima. Poi controlla il valore restituito dalla chiamata ricorsiva, e, se è avvenuto un cut-off, aggiorna la tabella per history heuristics, aggiorna l’array delle variazioni principali, e aggiorna il valore di alfa. Proseguendo con la ricorsione, la profondità rimanente (depth) descresce fino ad arrivare a zero. A quel punto viene richiamata un’altra funzione che implementa l’alfa-beta pruning, chiamata quiesce. Essa, a differenza di search, genera soltanto mosse di cattura; così facendo si è implementata la ricerca estesa per situazioni dove il punteggio della posizione può variare di molto (per l’appunto nel caso di catture di pezzi). In essa avviene anche la chiamata alla funzione di valutazione della posizione. 38
  • 39. Con il sistema così progettato si può ottenere grande profondità di calcolo nell’esplorazione della posizione nel caso il tempo concesso al computer sia elevato e contemporaneamente una risposta in breve tempo nel caso si volesse dare al software soltanto pochi secondi per decidere. 39
  • 40. Fig. 7 -Algoritmo semplificato di ricerca della mossa migliore 40
  • 41. Valutazione di una posizione Questo, dei tre problemi è sicuramente il più arduo: come valutare la bontà di una posizione? Molte sono le strategie di calcolo utilizzate negli scacchi normali. In ogni caso, la valutazione avviene con un algoritmo molto specifico, che funziona soltanto per lo speciale gioco degli scacchi classici. Tuttavia, alcuni fattori di cui si tiene conto negli scacchi possono essere utilizzati anche nel nostro caso. Sicuramente saranno sufficienti a giungere ad una soluzione iniziale soddisfacente, in modo che l’utente percepisca un certo grado di intelligenza nella scelta delle mosse da parte del computer, in quanto sono le più influenti anche nelle scacchi classici. In ogni caso si tratta sempre di algoritmi euristici, per i quali non riusciamo nemmeno a stimare il grado di ottimalità della soluzione trovata. Vediamo gli accorgimenti applicabili. Alternative progettuali • Bilanciamento degli schieramenti Il modo più intuitivo per assegnare un punteggio ad una posizione è contare quanto “materiale”, nel senso di pezzi, è ancora disponibile al bianco e quanto al nero. Inizialmente, entrambi dispongono di dieci pedoni, due unicorni, due cavalli, due alfieri, due torri, una regina, e naturalmente un re. La “potenza” di un pezzo è determinata unicamente dal suo grado di mobilità. La mobilità dipende sia da come esso può intrinsecamente muoversi (per regolamento) sia dalla sua posizione sulla scacchiera. 41
  • 42. Vediamo in una tabella le “potenzialità” di ciascun pezzo: Direzioni Caselle Pezzo Al Sui Al Sui Slide Note centro vertici centro vertici Può muoversi in un ristretto numero di caselle Unicorno 8 1 8 4 Sì rispetto le 125 totali (vedi fig.8) La sua mobilità è ostacolata soltanto da pezzi Cavallo 24 8 24 8 No presenti sulle caselle di destinazione e non da quelli sul tragitto (“salta” oltre i pezzi). Può muoversi su metà delle caselle totali Alfiere 12 3 32 12 Sì (quelle del colore della sua casa di partenza) Può muoversi sempre al massimo in 12 caselle, Torre 6 3 12 12 Sì a meno che non vi siano ostacoli sul tragitto. Ad ogni mossa possiede le mobilità addizionate Regina 26 7 52 28 Sì di un alfiere, di una torre e di un unicorno. Come tutti i pezzi è molto mobile al centro della scacchiera. Tuttavia è opportuno tenerlo Re 26 7 26 7 No “in salvo” in quanto la sua perdita comporta la sconfitta. Il suo valore sarà quindi infinito. Il pedone non è qui trattato, essendo un pezzo “speciale” sotto molti punti di vista. Alcuni chiarimenti sulla tabella: con “slide” stiamo ad indicare se un pezzo può “scivolare” di più caselle in una direzione, nel modo in cui lo fanno appunto l’alfiere, la torre, la regina e l’unicorno. Nella tabella si è tentato di quantificare intuitivamente in prima approssimazione la mobilità dei pezzi, confrontando il numero di direzioni e il numero di caselle in cui ciascun pezzo può muoversi nel caso esso si trovi su una scacchiera vuota in un vertice oppure al centro. Risulta immediatamente chiaro che alcuni pezzi sono sempre più mobili di altri, e tutti traggono beneficio quando sono spostati verso il centro della scacchiera. Ad esempio, è chiaro che l’unicorno è sicuramente il pezzo di minor valore fra quelli segnati in tabella: non solo non è molto mobile, ma non può nemmeno viaggiare su tutta la scacchiera, bensì solo su una minima parte di essa (circa ¼ delle caselle). Per contro, la regina è sicuramente il pezzo che vale di più (ricordiamo che il re ha valore idealmente infinito, quindi non conta), in quanto essa è il pezzo più mobile di tutti. 42
  • 43. Fig. 8 - I 4 “tipi” di unicorno diversi e il dettaglio delle loro mobilità. La cosa più difficile nella valutazione del materiale a disposizione è quantificare il valore dei singoli pezzi; negli scacchi classici si sono fissati, nei secoli di esperienza, alcuni valori di riferimento: o Pedone: 1 o Cavallo: 3 o Alfiere: 3 o Torre: 5 o Regina: 9 In realtà vi sono opinioni discordanti, spesso all’alfiere viene attribuito un valore maggiore che al cavallo. Poi verso il finale, ovvero quando ad un certo punto i pezzi in gioco cominciano ad essere “pochi”, i valori cambiano. Inoltre, il valore dei pedoni negli scacchi classici varia fortemente a seconda della posizione del pedone: se infatti esso può facilmente essere portato in zona di promozione varrà la pena difenderlo anche a costo di perdere ad esempio una torre ed un alfiere, per guadagnare una regina. 43
  • 44. Indipendentemente da tutto ciò, analizzando la nostra variante 3D, ci si accorge che i valori non vi si adattano: la torre, ad esempio, non è così “potente” come negli scacchi, perde infatti nella terza dimensione la sua funzione di “muro”; inoltre non è più così efficace nel sostegno dei pedoni. Lungi dagli obiettivi di questa tesi eseguire un approfondito studio sul punteggio base da assegnare ai pezzi, si è immaginata una sorta di “traduzione” dagli scacchi classici agli scacchi 3D, successivamente spiegata. • Sviluppo Il valore di un pezzo è tanto maggiore quanto maggiore è la sua influenza sulla scacchiera. Ad esempio una torre nella sua posizione originale non “controlla” nessuna casella, ovvero non può muoversi perché bloccata da altri pezzi del suo stesso schieramento. I pezzi hanno massima influenza quando sono mossi verso il centro della scacchiera. Per questi motivi, negli scacchi classici si costruiscono array di interi di dimensioni della scacchiera, i cui valori corrispondono a piccoli incrementi o decrementi di punteggio da applicare ai pezzi a seconda della loro posizione. In questo modo una scacchiera con pezzi sviluppati varrà di più rispetto ad una con i pezzi bloccati nelle posizioni originali. Nel caso nostro sarà semplice adattare una simile tecnica, non essendo obiettivo di questa tesi individuare i numeri ottimali per ottenere la valutazione più precisa: sarà sufficiente un array con somma zero e valori tanto cospicui da fare la differenza rispetto ad un metodo di valutazione privo di questo accorgimento. • Struttura dei pedoni Negli scacchi classici, in due dimensioni, risulta abbastanza semplice e intuitivo modificare il valore dei pedoni in funzione delle loro posizioni reciproche. Ad esempio, se in una colonna vi sono due o più pedoni dello stesso colore è naturale pensare che essi si ostacoleranno. Al comparire della terza dimensione tuttavia questo fenomeno tende praticamente a scomparire, muovendosi essi non solo lungo le colonne ma anche attraverso i piani. 44
  • 45. Omettiamo quindi di descrivere le varie strategie utilizzate per il calcolo del valore della struttura dei pedoni negli scacchi classici, perché esse non sarebbero adottabili per nostri scacchi 3D. • Posizione del re Negli scacchi classici, durante l’apertura (inizio della partita) e il mediogioco (parte del gioco difficilmente definibile, in essa i pezzi sono sviluppati e sono ancora “tanti”), il re è bene che stia dove si trova all’inizio, o ancora meglio “arroccato” ad uno dei due lati. Negli scacchi 3D non vi è manovra di arrocco, quindi è consigliabile che all’inizio esso rimanga nei pressi della casella iniziale, anche perché sarebbe inutile sprecare preziose mosse di sviluppo per spostare il lento re e portarlo alla mercé dell’esercito avversario. Nel finale della partita, quando i pezzi sono “pochi”, è essenziale, negli scacchi classici, che il re partecipi attivamente al combattimento. Infatti, in tutti i finali fondamentali (re e regina contro re, re e torre contro re, re e pedone contro re, ...) esso gioca un ruolo fondamentale. Di uguale importanza, se non maggiore, sarà negli scacchi 3D, nei quali è ancora più arduo, a causa della tridimensionalità, dare lo scacco matto. E’ quindi utile individuare un momento della partita oltre il quale il re “vale” di più se portato verso il centro dela scacchiera. La metodologia più semplice da utilizzare è valutare il materiale a disposizione dell’avversario: nel momento in cui i pezzi sono troppo pochi per nuocere seriamente, allora il re potrà essere portato allo scoperto. 45
  • 46. Soluzione adottata Bilanciamento degli schieramenti Anzitutto è opportuno fissare dei valori di base a ciascun tipo di pezzo. Ciò può essere fatto nel seguente modo: si possono definire i suddetti valori rapportando i gradi di mobilità dei pezzi negli scacchi classici con quelli degli stessi nel 3D, e variare il loro valore proporzionalmente. Nella tabella seguente si compara la mobilità di cavallo, alfiere, torre e regina negli scacchi 3D e negli scacchi classici. 3D Chess Scacchi Classici Pezzo Direzioni Caselle visitabili Direzioni Caselle visitabili Al centro Sui vertici Al centro Sui vertici Al centro Sui vertici Al centro Sui vertici Cavallo 24 8 24 8 8 2 8 2 Alfiere 12 3 32 12 4 1 13 7 Torre 6 3 12 12 4 2 14 14 Regina 26 7 52 28 8 3 27 21 Nella prossima tabella si riportano soltanto le caselle visitabili, rapportate però al numero di caselle totali della scacchiera (125 nel 3D, 64 negli scacchi classici). Caselle visitabili / Caselle totali Pezzo 3D Chess Scacchi Classici Al centro Sui vertici Al centro Sui vertici Cavallo 0.192 0.064 0.125 0.031 Alfiere 0.256 0.096 0.203 0.109 Torre 0.096 0.096 0.219 0.219 Regina 0.416 0.224 0.422 0.328 Si nota subito che il cavallo 3D risulta molto più mobile di quello classico. Al contrario, la torre è sensibilmente meno mobile, come ci si aspettava. 46
  • 47. Si osservi ora nella tabella seguente l’aumento di mobilità in prossimità dei vertici e in posizione centrale di ciascun pezzo dagli scacchi classici agli scacchi 3D: Fattore di incremento di mobilità nel 3D Pezzo Direzioni Caselle visitabili Caselle visitabili/Caselle totali Al centro Sui vertici Al centro Sui vertici Al centro Sui vertici Cavallo 3 4 3 4 1.536 2.064 Alfiere 3 3 2.286 1.714 1.261 0.881 Torre 1.5 1.5 0.857 0.857 0.438 0.438 Regina 3.25 2.333 1.857 1.333 0.986 0.683 A questo punto, applicando a questi fattori di incremento e ai valori negli scacchi classici un’opportuna funzione, sarà possibile ottenere dei valori da sperimentare nel motore per questi pezzi. Sicuramente il fattore di peso maggiore sarà il rapporto tra caselle visitabili e caselle totali, esso esprime la differenza di mobilità meglio rispetto agli altri fattori, essendo relativo. Un buon esempio potrebbe essere dare 10% di peso agli incrementi di direzioni, 14% di peso agli incrementi assoluti di caselle visitabili e 26% a quelli relativi. Perciò, definiti con e gli incrementi assoluti di direzione rispettivamente al centro e ai vertici e gli incrementi assoluti di caselle rispettivamente al centro e ai vertici e gli incrementi relativi di caselle rispettivamente al centro e ai vertici il fattore selezionato è: = [10 ∗ ( + ) + 14 ∗ ( + ) + 28 ∗ ( + ) ]/100 Esso andrà poi moltiplicato per il valore convenzionale del pezzo negli scacchi classici. Sostituendo i valori numerici si ottiene: o Cavallo: = 2.616 segue punteggio: 2.616 ∗ 3 = 7.848 o Alfiere: = 1.717 segue punteggio: 1.717 ∗ 3 = 5.151 o Torre: = 0.768 segue punteggio: 0.768 ∗ 5 = 3.839 o Regina: = 1.439 segue punteggio: 1.439 ∗ 9 = 12.95 47
  • 48. I valori così ottenuti saranno utilizzati come punteggio base dei pezzi. Mancano ancora tuttavia Unicorno e Pedone. Per quanto riguarda l’Unicorno, esso ha una mobilità che oscilla tra circa (sui vertici) e circa (al centro) rispetto a quello della regina (in base alle tabelle stilate). Inoltre, può muoversi al massimo su circa ¼ della scacchiera; questo fa di esso un pezzo estremamente debole e poco prezioso. Fissiamo il valore dell’unicorno a rispetto a quello della regina, cioè 2.16. Per i pedoni non si hanno parametri di valutazione da cui partire, se non il fatto che negli scacchi classici essi valgono 1 punto. Fissiamo il loro valore a 1 anche negli scacchi 3D. Pezzo valore Si riassume nella tabella a fianco i valori di base che Pedone 1 si è deciso di adottare, arrotondati ad una cifra Unicorno 2.2 Cavallo 7.8 decimale: sarebbe inutile essere molto precisi in Alfiere 5.1 quanto si tratta di valori praticamente arbitrari. Torre 3.8 Regina 12.9 Sviluppo I pezzi aumentano di valore quando vengono sviluppati, cioè sostanzialmente portati verso il centro della scacchiera. Questo vale quasi per tutti, a parte per i pedoni. Essi infatti valgono tanto quanto più si avvicinano alla zona di promozione. Il modo più semplice ed efficiente per ottenere questo effetto è utilizzare un array di interi grande quanto la scacchiera, inserendo un numero negativo nelle caselle “cattive” (vertici e bordi) e un numero positivo nelle caselle “buone” (centrali). Per ogni pezzo mosso si sommerà al suo valore il numero corrispondente alla casella in cui lo si vuole muovere. Questi accorgimenti si rivelano utili specialmente nella fase iniziale del gioco. Posizione del re Al re assegneremo in fase di realizzazione un valore incommensurabilmente alto rispetto a quello degli altri pezzi, naturalmente, perché perderlo equivale a perdere la partita. 48
  • 49. Il re necessita anche di due array di interi grandi come la scacchiera: uno per l’inizio della partita, per fare in modo che esso tenda a rimanere nella posizione iniziale, e uno per il finale. Si entrerà nel “finale” quando il valore dei pezzi avversari sarà uguale o inferiore a 1300, re escluso. Riepilogo funzionamento globale Fig. 9 - comportamento macroscopico del motore In figura 9 è illustrato il comportamento del motore dal punto di vista macroscopico: la UI comunica al motore la posizione della scacchiera, intesa come stato della partita. Se valida, si generano le mosse possibili. Se ne esistono, si ricerca la mossa migliore, e la si comunica alla UI. 49
  • 50. Progettazione dell’Interfaccia Scacchiera Tridimensionale Essa è la componente principale dell’interfaccia utente. Vale la pena stendere una breve introduzione sull’argomento grafica tridimensionale. La grafica tridimensionale La computer grafica 3D è un ramo della computer grafica che si basa sull'elaborazione di modelli virtuali in 3D da parte di un computer. Essa viene utilizzata insieme alla computer animation nella realizzazione di immagini visuali per cinema o televisione, videogiochi, ingegneria, usi commerciali o scientifici. Il termine può anche essere riferito ad immagini prodotte con lo stesso metodo. La grafica computerizzata tridimensionale è basilarmente la scienza, lo studio e il metodo di proiezione della rappresentazione matematica di oggetti tridimensionali tramite un'immagine bidimensionale attraverso l'uso di tecniche come la prospettiva e l'ombreggiatura (shading) per simulare la percezione di questi oggetti da parte dell'occhio umano. Ogni sistema 3D deve fornire due elementi: un metodo di descrizione del sistema 3D stesso ("scena"), composto di rappresentazioni matematiche di oggetti tridimensionali, detti "modelli", e un meccanismo di produzione di un'immagine 2D dalla scena, detto "renderer". Modelli 3D Oggetti tridimensionali semplici possono essere rappresentati con equazioni operanti su un sistema di riferimento cartesiano tridimensionale: per esempio, l'equazione x²+y²+z²=r² è perfetta per una sfera di raggio r. Anche se equazioni così semplici possono sembrare limitative, l'insieme degli oggetti realizzabili viene ampliato con una tecnica chiamata geometria solida costruttiva (CSG, constructive solid geometry), la quale combina oggetti solidi (come cubi, sfere, cilindri, ecc.) per formare oggetti più complessi attraverso le operazioni booleane (unione, sottrazione e intersezione): un tubo può ad esempio essere rappresentato come la differenza tra due cilindri aventi diametro differente. L'impiego di equazioni matematiche pure come queste richiede l'utilizzo di una gran quantità di potenza di calcolo, e non sono quindi pratiche per le applicazioni in tempo reale come videogiochi e simulazioni. Una tecnica più efficiente, ma che permette un minore livello di dettaglio, per modellare oggetti consiste nel rilevare solo alcuni punti dell'oggetto, senza informazioni sulla curva compresa tra di essi. Il risultato è chiamato 50
  • 51. modello poligonale. Questo presenta "faccette" piuttosto che curve, ma sono state sviluppate tecniche d rendering per ovviare a questa perdita di di dati. Delle superfici poligonali di un modello senza informazioni sulla curvatura possono essere comunque raffinate per via algoritmica in superfici perfettamente curve: questa tecnica è chiamata "superfici di su suddivisione" (subsurfing), perché la superficie viene suddivisa con un processo iterativo , in più superfici, sempre più piccole, fedeli alla curva interpolata e che vanno a comporre un'unica superficie sempre più liscia. Creazione della scena Una scena si compone di "primitive" (modelli tridimensionali che non ompone possono essere ulteriormente scomposti). Le primitive sono generalmente descritte all'interno del proprio sistema di riferimento locale, e vengono posizionate sulla scena attraverso opportune trasformazioni. Le trasformazioni affini più impiegate, come omotetia, zioni. rotazione e traslazione, possono essere descritte in uno spazio proiettivo con una matrice 4x4: esse si applicano moltiplicando la matrice per il vettore a quattro componenti che rappresenta ogni punto di controllo delle ogni curva. La quarta dimensione è denominata coordinata omogenea. Rendering Il rendering è il processo di produzione dell'immagine finale a partire dal modello matematico del soggetto (scena). Esistono molti algoritmi di rendering, ma tutti implicano la proiezione dei modelli 3D su una superficie 2D. Gli oggetti tridimensionali devono essere proiettati idealmente sulla superficie bidimensionale del monitor. Il tipo di proiezione p usato è la prospettica, anche se ad esempio nei più , sistemi CAD e CAM si utilizza la proiezione ortogonale. stemi La differenza fra le due è che nella prospettica gli oggetti più lontani sono visualizzati di dimensioni minori rispetto a quelli più vicini all’ “occhio”. I minori software rendono la prospettiva moltiplicando una costante di dilatazione k moltiplicando elevata all’opposto della distanza dall’osservatore. Se k = 1 non vi è prospettiva. All’aumentare di k aumenta la distorsione prospettica. Fig. 10 Un punto di fuga - Due punti di fuga - Tre punti di fuga Una trattazione dettagliata della matematica riguardande la proiezione prospettica esula dagli scopi di questo testo. 51
  • 52. Per una spiegazione intuitiva è sufficiente immaginare una videocamera che si muove all’interno di uno spazio tridimensionale. In questo spazio sono contenuti i modelli tridimensionali, nel nostro caso si tratta della scacchiera e dei pezzi. L’utente sarà in grado di controllare il “movimento” di questa videocamera ideale all’interno dello spazio 3D in modo tale da poter visualizzare comodamente. Fig. 11 - Field of view (campo visivo) della videocamera In figura 11 è descritto il campo visivo che viene sottoposto all’utente: è di forma tronco-conica (frustum), tutto ciò che non è contenuto in esso non viene proiettato (sulla base minore, che rappresenta la superficie dello schermo) e quindi non viene visualizzato. Progettazione dei modelli Vi sono principalmente quattro modi per rappresentare un modello 3D: • Modelli poligonali – Un insieme di punti nello spazio tridimensionale, detti vertici, vengono connessi da segmenti in modo da formare una maglia (mesh). La maggior parte dei modelli 3D sono costruiti in questo modo per la loro flessibilità e velocità di rendering. Tuttavia, i poligoni sono piani, quindi con essi le curvature possono soltanto essere approssimate utilizzando un gran numero di poligoni. 52
  • 53. NURBS – Le superfici NURBS sono definite da curve spline, cioè parametrizzate da punti di controllo pesati. La curva segue (senza necessariamente interpolarli) i punti. Aumentando il peso di un punto si farà in modo che la curva passi più vicino ad esso. In questo modo si riescono ad ottenere superfici estremamente lisce. • Splines & Patches – Come le NURBS, splines and patches dipendono da line curve per definire la superficie visibile. Sono una via di mezzo fra NURBS e poligoni in termini di flessibilità e facilità di utilizzo. • Primitives modeling – Consiste nell’utilizzare primitive geometriche come sfere, cilindri, coni o cubi, come “mattoni” per modelli più complessi. Si ottengono così costruzioni veloci, e le forme sono matematicamente precise. Richiede però di definire geometricamente il modello da costruire; ciò non è sempre possible o facile da ottenere. Nel nostro caso, trattandosi di pezzi degli scacchi, quindi di forme geometriche non troppo complesse, i metodi migliori sarebbero modelli poligonali o primitives modeling. Quest’ultimo sembrerebbe il più indicato, tuttavia, sebbene sia controintuitivo, i modelli ottenuti sarebbero certamente più grezzi: modellare tutto tramite primitive, infatti, implicherebbe poter utilizzare soltanto curve e forme definibili geometricamente. Invece, tramite poligoni, si avrà più rapidità al momento della costruzione dei modelli (si potranno comporre facilmente tramite una serie di punti); l’arrotondamento delle superfici si potrà ottenere per suddivisioni successive, mantenendo inoltre la massima compatibilità (i formati poligonali sono i più comuni al momento). Pezzi Consisteranno in sette modelli poligonali, ciascuno per un tipo di pezzo. Il colore e il posizionamento sulla scacchiera verranno dati loro dal programma di gioco. Scacchiera Per costruirla si utilizzerà un mattoncino fondamentale (un parallelepipedo molto esteso in larghezza rispetto alla sua altezza, una sorta di “mattonella”), ripetuto 125 volte a colorazione alterna. Si visualizzeranno cioè cinque scacchiere piane sovrapposte, come in figura 1. 53
  • 54. Interazione con l’utente Mouse – Movimentazione dei pezzi Per muovere i pezzi si deve poter, tramite mouse, selezionare il pezzo da muovere e successivamente selezionare la casella in cui muoverlo. In figura 12 è illustrato l’algoritmo ad alto livello per la selezione del pezzo. L’utente fa click sulla schermata dell’interfaccia 3D No Ha fatto click su un Sì Attesa di pezzo? interazione utente No No Ha fatto click su una Il pezzo è suo? casella? No Sì Sì Al momento è Il pezzo può evidenziato un muovere? pezzo? Sì Sì Togliere l’evidenziazione Evidenziare il pezzo e le al pezzo precedentemente La casella è caselle in cui esso può selezionato ed alle caselle No evidenziata? muovere in cui esso può muovere (Selezione Pezzo) (Deselezione Pezzo) Sì Muovere il pezzo nella casella (Spostamento Pezzo) Fig. 12 - Flow chart raffigurante il loop di input da mouse per l'utente 54
  • 55. Il problema fondamentale però è che l’utente fa click su un punto di una superficie piana; a partire dalle coordinate piane del click bisogna risalire a quale punto dello spazio esso corrisponde. I modelli che posizioniamo nello spazio sono costituiti da triangoli. Si tratterà quindi di determinare, ciclando su tutti i modelli contenuti nello spazio, quale di essi è il più vicino alla camera fra quelli intersecati dalla semiretta avente origine nel punto in cui l’utente ha fatto click. I modelli sono composti da triangoli. Bisognerà quindi procedere in questo modo: 1. Per ogni modello controllare se la sfera di raggio minimo che lo contiene tutto interseca la semiretta in questione. 2. Se la sfera è intersecata bisognerà testare, triangolo per triangolo, se vi sono triangoli componenti il modello che intersecano la semiretta. 3. Se ve ne sono, salvare quello di distanza minima dalla camera. Se minore di quelli precedentemente trovati, memorizzare il modello a cui appartiene. In questo modo si troverà il modello più vicino alla camera intersecato dalla semiretta. Il problema più complesso è individuare quali triangoli di un modello, ammesso che ve ne siano, siano intersecati dalla semiretta. Un buon sistema per ottenere questo risultato è utilizzare un algoritmo il cui uso è abbastanza diffuso per casi come questo, ideato da Tomas Moller e Ben Trumbore nel 2003 chiamato “Fast, Minimum Storage Ray-Triangle Intersection". Questo algoritmo permette, senza perdita di velocità rispetto agli algoritmi precedenti, di risparmiare memoria nel suo utilizzo. In appendice si allega il documento originale che sarà utilizzato per implementare l’algoritmo nella parte di realizzazione. Tastiera – Camera Per far in modo che il gioco sia il più controllabile possibile, si progettano due modalità di visualizzazione (camera). Una il prima persona, che permette cioè di spostare liberamente la camera per mezzo della tastiera nello spazio contenente la scacchiera. 55
  • 56. Un’altra invece vincolata alle righe e alle colonne della scacchiera: ovvero, essendovi, 5 righe e 5 colonne per ciascuno dei 5 piani, con (5 + 5) ∗ 5 = 50 posizioni; ad ogni spostamento di posizione la si farà puntare parallelamente alla direzione della riga o colonna, ma sarà possibile ruotarla sul posto per facilitare la visualizzazione. L’utente potrà in qualsiasi momento cambiare camera passando da totale libertà di movimento a controllo più vincolato. Comunicazione Interfaccia-Motore Differenza sostanziale tra le realizzazioni di Interfaccia e Motore sarà la tecnologia d’implementazione; per la prima è necessaria una piattaforma in grado di supportare la grafica 3D e che renda il più possibile agevole lo sviluppo di videogame, mentre per il secondo è importante l’efficienza di calcolo. Perciò, per l’Interfaccia sarà certamente opportuno utilizzare una API per il game-programming. Ve ne sono tante disponibili, ma non molte soddisfano il requisito della portabilità che è stato richiesto (vedi Analisi, pag. 17). Per quanto riguarda il Motore sarà più utile una realizzazione che non giri su macchina virtuale; in questo modo si avrà il massimo dell’efficienza. Quindi un linguaggio compilato. Per soddisfare la portabilità basterà usare un linguaggio molto diffuso, in modo che esistano compilatori per esso almeno per le piattaforme più utilizzate; certo, ci sarà bisogno di ricompilare per passare da un ambiente ad un altro, ma il requisito di efficienza è certamente più importante per ottenere un Motore “intelligente” in grado di tener testa al giocatore umano. La comunicazione fra i due è sufficiente che sia in una sola direzione, cioè dall’Interfaccia al Motore, o vice versa; non ponendo vincoli in questo frangente si lascia maggior libertà in fase di realizzazione. 56
  • 57. Realizzazione Tecnologie utilizzate Microsoft XNA (Xbox New Architecture) Si tratta di un inseme di tool inseriti in un ambiente di runtime “managed” il cui scopo è facilitare lo sviluppo di videogame sollevando lo sviluppatore dall’onere di dover scrivere codice ripetitivo. Comprende: • XNA Framework Si basa sull’implementazione del .NET Compact Framework 2.0 per Xbox 360 e su .NET Framework 2.0 per Windows. E’ disponibile per Windows XP, Windows Vista e Xbox 360; permette di scrivere i videogame per il runtime di XNA: questo fa sì che essi possano girare su qualsiasi piattaforma a patto che questa supporti il runtime. L’utilizzo di questa tecnologia quindi ci permetterebbe di portare senza troppe difficoltà il gioco su piattaforme diverse, e ci solleva inoltre dall’onere di reinventare ciò che nel gergo del game development si definisce “game loop” (e tutto ciò che esso implica). • XNA Build E’ un insieme di tool per la gestione degli “asset” di gioco (icone, file audio, modelli 3D, ...). Aiuta a definire la cosiddetta game asset pipeline. Quest’ultima descrive il processo attraverso il quale i contenuti vengono trasformati in forme utilizzabili dall’engine del gioco. XNA Build permette di personalizzare a vari livelli ogni stadio della suddetta pipeline. • Mono.XNA E’ una implementazione cross platform di XNA gaming framework. Prende il nome da Mono, ormai nota implementazione cross platform di .NET framework Gira su Windows, MacOS e Linux e usa OpenGL per la grafica 3D. Al momento è ancora in fase di realizzazione. Questo aspetto di XNA ci assicura la portabilità richiesta nei requisiti non funzionali. XNA sarà quindi utilizzato per la realizzazione dell’Interfaccia. 57
  • 58. Blender E’ un’applicazione per grafica 3D rilasciata sotto forma di software libero sotto GNU General Public License. Può essere usato per modeling e molto altro ancora, ed è disponibile per Linux, Mac Os X e Windows. Questo lo rende più che sufficiente per lo scopo di questa tesi. Con Blender saranno costruiti i modelli 3D di pezzi e scacchiera. ANSI C E’ lo standard emesso dall’American National Standards Institute (ANSI) per il linguaggio di programmazione C. ANSI C è supportato praticamente da tutti i compilatori più utilizzati; perciò, ogni programma scritto in C standard garantisce la compilazione su qualsiasi piattaforma che abbia una conforme implementazione del C. Il motore, essendo nulla più che una collezione di algoritmi e strutture dati, potrà quindi essere costruito in ANSI C per massimizzarne la portabilità pur mantenendo alta l’efficienza, trattandosi di un linguaggio compilato. Managed/Unmanaged Interoperability Realizzando l’Interfaccia in XNA, si utilizzerà C#, un linguaggio Microsoft facente parte del CLR di .NET. Si tratterà quindi di codice che gira in macchina virtuale, ovvero managed. Per contro, il Motore sarà scritto in ANSI C. Per la realizzazione in Microsoft Windows verrà compilato come dll C++ ed esporrà delle funzioni che potranno essere richiamate dall’Interfaccia tramite il sistema di interoperabilità tra codice managed e unmanaged. Si tratterà quindi di invocare codice unmanaged dal codice managed. Vi sono alcune alternative per compiere questa operazione: • Platform Invoke (detta anche P/Invoke) permette di richiamare funzioni scritte in qualsiasi linguaggio unmanaged a patto che la signature sia ridichiarata in codice managed. • COM interop permette di richiamare component COM da qualsiasi linguaggio managed. • C++ interop (anche detta It Just Works, IJW) è una caratteristica specifica del C++ che permette di utilizzare direttamente API COM e non solo. E’ più potente di COM interop ma richiede più cura. 58
  • 59. Su MSDN si raccomanda di scegliere seguendo il flow-chart riportato in figura 13. Fig. 13 - Flow chart per la scelta della tecnologia per interoperabilità managed/unmanaged La dll del motore non sarà certo una COM API, essendo scritta in C. Con complex API si intende un’API che ha le signature dei metodi scritte in modo difficile da dichiarare in codice managed. Essendo l’API ancora da scrivere, faremo in modo che ciò non avvenga. L’API una volta scritta rimarrà tale; o comunque il numero di funzioni sarà sufficientemente piccolo (dell’ordine della decina) che anche se varierà non sarà difficile propagare la variazione anche al codice managed. Quindi la tecnologia per la comunicazione tra Interfaccia e Motore sarà Platform Invoke. 59
  • 60. Realizzazione del Motore Il motore è stato realizzato in ANSI C, non essendovi necessità o particolari benefici nel ricorrere all’OOP in un caso come questo. Esso consiste di 5 file header “.h” e 8 file “.c”. Header files defs.h defs.h, contenente la definizione delle costanti, dei tipi non primitivi utilizzati e delle variabili globali. Qui sotto si riporta uno spezzone di codice contente le strutture dati utilizzate per la memorizzazione delle mosse generate. moveBytes contiene l’indice della casella di arrivo e partenza di una mossa (from e to), il pezzo a cui viene promosso nel caso di mossa di promozione (promote), una serie di bit indicante il tipo di mossa (bits: i primi 3 bit di questo char vengono usati per indicare se si tratta di una mossa di cattura, e/o di un pedone, e/o di promozione). typedef struct { int from; int to; int promote; char bits; } moveBytes; In union con un int in modo che si riesca a compararle rapidamente. typedef union { moveBytes b; int u; } moveUni; gener_t associa la mossa ad un punteggio (score) utilizzato per il sorting nell’implementazione dell’history heuristics. typedef struct { moveUni m; int score; } gener_t; history_t è invece un elemento dello stack usato per mantenere la storia della partita, in modo da poter ritornare indietro di una mossa. 60