Smazzare una partita di bridge Classe Carta Implementate una classe di nome Carta che rappresenta una carta da gioco. Internamente, la classe deve mantenere un attributo di tipo intero che contiene il seme (0=cuori, 1=quadri, 2=fiori, 3=picche) e un attributo di tipo intero che contiene il valore (1=asso, 2=due, eccetera). La classe deve implementare l'interfaccia Comparable<Carta> , e deve avere i seguenti costruttori e metodi: Carta(int seme, int valore) : costruisce la carta corrispondente. boolean equals(Object x) : restituisce true se valore e seme sono uguali. int compareTo(Carta x) : ordina le carte prima per seme, poi per valore; l'ordine dei semi è il classico “come quando fuori piove”. Il metodo deve quindi prima confrontare il seme, e, se è possibile decidere sulla base del che una carta è minore o maggiore di un'altra, restituire il valore corretto. Nel caso i due semi siano uguali, deve restituire l'ordine per valore, tenendo conto del fatto che l'asso è la carta di massimo valore. String toString() : restituisce una stringa di due caratteri formata da valore e seme, dove il seme deve essere una singola lettera maiuscola (conviene scrivere un metodo statico contenente uno switch che, dato l'indice di un seme, restituisce la stringa corretta) e il valore deve essere numerico per tutte le carte ad eccezione di asso, jack, queen e king, che vanno denotati con l'iniziale maiuscola (conviene scrivere un metodo statico contenente uno switch che, dato l'indice di un valore, restituisce la lettera corretta). Extra: se ci riuscite, utilizzate i caratteri Unicode estesi ♥, ♦, ♣ e ♠ invece delle iniziali. Sperimentate la classe, realizzando un programma di test che permette di inserire carte immettendo seme e valore. Per leggere seme e valore utilizzate Scanner.nextToken() e Scanner.nextInt() in un ciclo che termina quando Scanner.hasNext() restituisce false. Classe Giocatore Implementate una classe di nome Giocatore che rappresenta un giocatore. Internamente, la classe deve mantenere un attributo di tipo stringa che contiene il nome del giocatore, e una lista di carte che rappresenta le carte correntemente in mano al giocatore. La classe deve avere i seguenti costruttori e metodi: Giocatore(String nome) : costruisce un giocatore con nome dato. void prendi(Carta carta) : aggiunge la carta argomento a quelle in mano al giocatore. String toString() : restituisce stringa contenente il nome del giocatore seguito dalle carte in suo possesso, stampate separandole con uno spazio. Prima di costruire la stringa, ordinate la lista delle carte utilizzando Collections.sort() in modo che compaiano in ordine di seme e valore. Extra: se ci riuscite, inserite due spazi tra due carte se hanno seme diverso. Classe Smazzata Laboratorio di programmazione (16 dicembre 2015) Implementate una classe di nome Smazzata contenente un metodo main() che genera una permutazione aleatoria di un mazzo completo, e distribuisce le carte del mazzo a quattro giocatori inseriti dall'utente. La classe deve stampare i quattro giocatori (in modo che si possa vedere la smazzata). Per ottenere una permutazione aleatoria procedete come segue: 1. Costruite utilizzando due cicli annidati un vettore mazzo di 52 carte contenente un mazzo ordinato. 2. Permutate il vettore utilizzando un generatore di numeri pseudocasuali: Collections.shuffle( Arrays.asList( mazzo ), new Random() ); Il metodo static Collection.shuffle() permuta in maniera aleatoria una lista, dato un generatore di numeri pseudocasuali ( new Random() ). Il metodo Arrays.asList() restituisce una classe lista involucro basata sull'array argomento. 3. A questo punto, create quattro giocatori utilizzando nomi letti da riga di comando (cioè dal vettore argomento del main() ), e assegnate a ciascun giocatore 13 carte come farebbe un mazziere: al primo giocatore le carte di indice 0, 4, 8, ecc.. Al secondo giocatore le carte di indice 1, 5, 9, etc. E così via. Per finire, stampate i giocatori, in modo da poter controllare che le loro smazzate siano corrette e stampate nell'ordine corretto. Esempio di funzionamento java Smazzata Marco Mario Marta Maria Marco 8♥ Q♥ A♥ 6♦ J♦ K♦ A♦ 2♣ 5♣ 8♣ K♣ A♣ 9♠ Mario 2♥ 4♥ 5♥ 10♥ J♥ K♥ 2♦ 8♦ 9♦ 9♣ 5♠ 6♠ A♠ Marta 3♥ 6♥ 4♦ 10♦ 3♣ 4♣ 6♣ 7♣ J♣ 2♠ 7♠ 8♠ 10♠ Maria 7♥ 9♥ 3♦ 5♦ 7♦ Q♦ 10♣ Q♣ 3♠ 4♠ J♠ Q♠ K♠ java Smazzata Marco Mario Marta Maria Marco 3♥ 4♥ 7♥ 5♦ 8♦ 10♦ 7♣ 9♣ Q♣ 2♠ 4♠ 8♠ A♠ Mario 9♥ A♥ 2♦ 3♦ 6♦ 7♦ 2♣ 3♣ 4♣ A♣ 3♠ 6♠ K♠ Marta 2♥ Q♥ 4♦ Q♦ A♦ 6♣ 8♣ J♣ K♣ 5♠ 7♠ 10♠ J♠ Maria 5♥ 6♥ 8♥ 10♥ J♥ K♥ 9♦ J♦ K♦ 5♣ 10♣ 9♠ Q♠ java Smazzata Marco Mario Marta Maria Marco 9♥ Q♥ K♥ 7♦ 9♦ A♦ 10♣ 3♠ 4♠ 5♠ 6♠ 7♠ 10♠ Mario 6♥ J♥ A♥ 3♦ 6♦ J♦ Q♦ 3♣ 4♣ 8♣ J♣ Q♠ K♠ Marta 4♥ 7♥ 10♥ 5♦ 8♦ K♦ 5♣ 6♣ 9♣ 2♠ 8♠ 9♠ J♠ Maria 2♥ 3♥ 5♥ 8♥ 2♦ 4♦ 10♦ 2♣ 7♣ Q♣ K♣ A♣ A♠ Foresta In questo esercizio rappresenteremo un insieme di organismi — animali erbivori, animali carnivori e piante — che si divorano l'un l'altro. Per farlo, implementate la seguente gerarchia di classi: Organismo È una classe astratta che rappresenta un organismo di qualunque tipo. Animali e piante saranno sue sottoclassi. Dovrà Laboratorio di programmazione (16 dicembre 2015) implementare l'interfaccia Comparable<Organismo> , e avrà tre attributi privati, String nome , double peso e boolean vivo , Un metodo astratto: boolean puoMangiare(Organismo cibo) che restituirà true sse questo organismo può mangiare cibo . E i seguenti metodi: Costruttore Organismo(String nome, double peso) che costruisce un organismo, vivo, di dato nome e peso. String toString() che restituisce il nome e tra parentesi il peso; per esempio, Leone(100.0 kg) . double getPeso() che restituisce il valore dell'attributo peso . boolean isVivo() che restituisce il valore dell'attributo vivo . void uccidi() che imposta l'attributo vivo a false . boolean mangia(Organismo cibo) che agisce così: se questo organismo moltiplicato per puoMangiare l'organismo cibo , aumenta il proprio peso del peso di cibo 0.9 ; quindi, ne invoca il metodo cibo.uccidi() ; infine, stampa organismo1 ha mangiato organismo2 , usando i toString() dei due organismi. Restituisce true . Altrimenti, restituisce false . int compareTo(Organismo altro) deve rispettare l'interfaccia di Comparable , confrontando i due organismi in base al proprio peso. Pianta Sottoclasse di Organismo che rappresenta una pianta. Il costruttore non ha argomenti, e il nome è sempre Pianta mentre il peso è sempre 1. boolean puoMangiare(Organismo cibo) restituirà sempre false . AnimaleErbivoro Sottoclasse di Costruttore Organismo che rappresenta un animale erbivoro. AnimaleErbivoro(String nome) ; il peso è sempre 10. boolean puoMangiare(Organismo cibo) controllerà se cibo instanceof Pianta , e in quel caso restituisce true . AnimaleCarnivoro Sottoclasse di Costruttore Organismo che rappresenta un animale carnivoro. AnimaleCarnivoro(String nome) ; il peso è sempre 20. boolean puoMangiare(Organismo cibo) restituirà e cibo non è una pianta; usate compareTo() . true se questo organismo è più pesante di cibo Laboratorio di programmazione (16 dicembre 2015) Ambiente Questa classe contiene un metodo main() che fa quanto segue: 1. Chiede all'utente che tipo di organismo vuole creare (P/E/C), poi ne chiede il nome, se necessario, e inserisce l'organismo in un ArrayList<Organismo> , finchè l'utente non inserisce F (finito). 2. Quindi, esegue quanto segue al massimo 1000 volte, e finché c'è almeno un organismo vivo. 1. Ordina l' ArrayList per peso. 2. Scorre gli organismi uno per uno, e per l'organismo vivo i , se i non è una pianta (usate instanceof ): scorre l'array list nello stesso ordine, considerando di volta in volta un organismo vivo j ; invoca il metodo mangia di i su j ; se questo va a buon fine, interrompe la ricerca di cibo e passa all'organismo i successivo; altrimenti, prova con il j successivo, finché non ha terminato tutto l'array; se non riesce a mangiare nulla, l'organismo i muore. Incapsulate parti di questo main in metodi statici come ritenete opportuno. Esempio di esecuzione Che organismo vuoi inserire? [P/C/E/F] P Che organismo vuoi inserire? [P/C/E/F] P Che organismo vuoi inserire? [P/C/E/F] P Che organismo vuoi inserire? [P/C/E/F] P Che organismo vuoi inserire? [P/C/E/F] P Che organismo vuoi inserire? [P/C/E/F] P Che organismo vuoi inserire? [P/C/E/F] E Che nome ha questo organismo? Gazzella Che organismo vuoi inserire? [P/C/E/F] E Che nome ha questo organismo? Gazzella Che organismo vuoi inserire? [P/C/E/F] E Che nome ha questo organismo? Gazzella Che organismo vuoi inserire? [P/C/E/F] C Che nome ha questo organismo? Leone Che organismo vuoi inserire? [P/C/E/F] F Leone(29.0 kg) ha mangiato Gazzella(10.0 kg) Gazzella(10.9 kg) ha mangiato Pianta(1.0 kg) Gazzella(10.9 kg) ha mangiato Pianta(1.0 kg) Leone(38.81 kg) ha mangiato Gazzella(10.9 kg) Gazzella(11.8 kg) ha mangiato Pianta(1.0 kg) Leone(49.43000000000001 kg) ha mangiato Gazzella(11.8 kg) Life Life (vita, in inglese) è un automa cellulare inventato dal matematico John Conway per studiare un'emulazione elementare dei processi vitali e diventato famoso dopo la sua descrizione su Scientific American nel 1971. L'automa è composto da una matrice di n righe e m colonne di elementi, dette cellule, che possono essere in due stati: vive o morte. Attorno a ogni cellula ci sono otto cellule adiacenti (nord, sud, est, ovest, nord-ovest, nord-est, sud-ovest e sud-est): si noti che la matrice è pensata come un toro, e quindi il bordo destro è adiacente a quello sinistro, così come il bordo superiore è adiacente a quello inferiore. Per esempio, la cellula di coordinate (0, 0) ha a nord la cellula (n – 1, 0) e a ovest la cellula (0, m – 1). Le regole di evoluzione della vita in Life sono molto semplici: se una cellula è viva, sopravvive all'istante di tempo successivo se nel suo intorno ci sono due o tre cellule vive; altrimenti muore per solitudine o sovraffollamento. Se una cellula è morta e nel suo intorno ci sono esattamente tre cellule vive diventa viva all'istante successivo. Dovete scrivere una classe che simuli Life e visualizzi graficamente l'evoluzione di una matrice le cui dimensioni sono specificate sulla riga di comando. Per ottenere la visualizzazione grafica, potete stampare la matrice emettendo uno spazio per le cellule morte, e un altro carattere (per esempio *) per le cellule vive. Il vostro programma deve prendere in input sulla riga di comando tre parametri: il numero n, il numero m e una probabilità p tra 0 e 1 che verrà utilizzata per inizializzare la matrice: ogni cellula sarà viva indipendentemente con probabilità p. Suggerimenti 1. Per realizzare la simulazione, dovete utilizzare due matrici di booleani. La prima matrice rappresenta la configurazione corrente (vero/viva, falso/morta). Da essa potete calcolare la configurazione successiva, che memorizzerete nella seconda matrice. A questo punto potrete ricopiare la seconda matrice sulla prima, visualizzarla e ricominciare daccapo. Notate che per ricopiare la matrice vi serve una coppia di cicli annidati. 2. Per ottenere l'effetto di considerare la matrice un toro, dovete incrementare o decrementare le coordinate utilizzando l'aritmetica modulare. Ad esempio, se la matrice si chiama a , le celle adiacenti ad a[x][y] sono a[(x+1) % n][y] a[(x+n-1) % n][y] a[x][(y+1) % m] a[x][(y+m-1) % m] a[(x+1) % n][(y+1) % m] a[(x+n-1) % n][(y+1) % m] a[(x+1) % n][(y+m-1) % m] a[(x+n-1) % n][(y+m-1) % m] Attenzione: non eliminate i +n e i +m apparentemente ridondanti; sono necessari perché l'implementazione del modulo sui numeri negativi è errata. 3. Ricordatevi che per convertire i parametri sulla riga di comando in interi vi serve Integer.parseInt() , e per convertirli in double Double.parseDouble() . 4. Per stabilire se una cellula deve essere viva nello stato iniziale potete usare il test if (Math.random() < p) ... che restituirà vero con probabilità p. 5. È utile, per quanto possibile, strutturare il programma: create un metodo statico visualizza() che stampa la matrice come richiesto in cima allo schermo, e provatelo separatamente. 6. Per matrici piccole, la visualizzazione potrebbe essere troppo rapida: inserite in tal caso l'istruzione Thread.sleep(100); all'interno del ciclo principale (ritarda l'esecuzione di un decimo di secondo). È necessario che il main() abbia la dichiarazione throws Exception . 7. Dato che per ottenere un effetto di animazione è necessario sovrascrivere la visualizzazione precedente, potete usare l'istruzione System.out.println("\x1b[H"); per posizionare il cursore nell'angolo in alto a sinistra senza cancellare lo schermo, e l'istruzione System.out.println("\x1b[H\x1b[2J"); per cancellare l'intero schermo all'inizio dell'esecuzione del programma. 8. Una volta che il programma è ben collaudato, potete provare ad aumentare le dimensioni massime possibili a 1000×1000; scegliendo poi un corpo molto piccolo potete visualizzare animazioni di grandi dimensioni. 9. Il programma non deve terminare mai (come la vita). Potrete interromperlo comunque con Control-C (come nella fine del mondo). Suggerimenti aggiuntivi Questi suggerimenti tolgono un po' del divertimento che c'è nel risolvere un problema da soli, e quindi sono a parte. 1. Se vi considerate particolarmente furbi, potete utilizzare l'aritmetica modulare per scambiare a ogni iterazione il ruolo delle due matrici, evitando così la copia. 2. Il modo più semplice di contare quante cellule vive sono presenti attorno a una cellula data è quello di utilizzare due cicli annidati che vanno da –1 a 1 inclusi, i cui contatori rappresentano lo spostamento per righe e per colonne: int c = -a[x][y]; for(int dx=-1; dx<=1; dx++) for(int dy=-1; dy<=1; dy++) c += a[(x+dx+n) % n][(y+dy+m) % m];