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];