• Non ci sono risultati.

1.2 Hello World!

4.4.1 Codice

1.

far sì che Tavoloimplementi l’interfaccia Iterable<Card>

Implementare l’interfaccia

Iterable<Card>

vuol dire essenzialmente fornire un metodo

Iterator<Card>

iterator(), come del resto suggerito da IDEA con Implement methods. public class Tavolo implements Iterable<Card> {

private List<Card> scoperte = new ArrayList<>();

@Override

public Iterator<Card> iterator() {

return scoperte.iterator();

} // ...

}

2. completare Giocatore.turnoin maniera che ogni giocatore giochi una carta in maniera conforme alle regole del gioco

Attualmente

Giocatore.turno

sceglie una carta (

aCard

, chiamando

strategia.get

Cardche sceglie fra le carte che il giocatore ha in

mano

), ma non la gioca.

public void turno() { assert strategia != null;

final Card aCard = strategia.getCard(mano);

//TODO GIOCARE LA CARTA rispettando le regole

}

Per giocarla secondo le regole bisogna seguire il procedimento indicato nel testo dell’eser-cizio:

se tale carta ha lo stesso valore di una sul tavolo, il giocatore pone le due carte con lo stesso valore in un mazzetto davanti a sé in modo che tutti possano vedere l’ultima “presa”. Se non c’è una carta dello stesso valore sul tavolo e invece uno dei mazzetti degli altri giocatori ha in cima una carta dello stesso valore di quella giocata, il giocatore può “rubare” il mazzetto all’avversario, sottraendolo all’avversario e ponendolo insieme alla carta giocata in cima al proprio. Altrimenti la carta viene posta insieme alle altre scoperta sul tavolo.

Cioè:

la carta è sul tavolo bisogna “prendere”: il mazzetto del giocatore aumenta di 2, in cima c’è il

Rank

giocato e la carta viene tolta dal

Tavolo

;

la carta non è sul tavolo, ma c’è un mazzetto bisogna “rubare”: il mazzetto del gioca-tore aumenta del punteggio del

Giocatore

derubato +1 e in cima c’è il

Rank

giocato;

il punteggio del derubato torna a zero e il suo mazzetto vuoto;

la carta non è sul tavolo e non c’è un mazzetto la carta viene messa sul tavolo.

In Java:

public void turno() { assert strategia != null;

final Card aCard = strategia.getCard(mano);

mano.remove(aCard);

if (Partita.TAVOLO.inMostra(aCard)){

this.prendiDalTavolo(aCard);

return;

}for (Giocatore giocatore : altri) {

if (sePuoiRuba(giocatore, aCard)) return;

}

4.4 Soluzioni

Partita.TAVOLO.metti(aCard);

}

private boolean sePuoiRuba(Giocatore altro, Card aCard) { if (altro.getMazzettoTop() == aCard.getRank()){

punti += altro.cediMazzetto() + 1;

mazzettoTop = aCard.getRank();

return true;

}

return false;

}

private int cediMazzetto() { int ris = getPunti();

punti = 0;

mazzettoTop = null;

return ris;

}

private void prendiDalTavolo(Card aCard) { Partita.TAVOLO.prendi(aCard);

punti += 2;

mazzettoTop = aCard.getRank();

}

Vale la pena di notare una peculiarità della programmazione orientata agli oggetti: i due metodi (privati)

prendiDalTavolo

e

cediMazzetto

sono definiti nella stessa clas-se (

Giocatore

): sono due computazioni che tutti gli oggetti di tipo

Giocatore

sanno svolgere. In ciascuna effettiva esecuzione del metodo

turno

però, i due metodi saranno presumibilmente eseguiti su oggetti diversi (cioè due istanze della classe, con due identità distinte) perché un giocatore non ruba mai il proprio mazzetto. Quindi: in un’esecuzione di

turno

in cui viene chiamato

prendiDalTavolo

,

turno

e

prendiDalTavolo

operano sul medesimo oggetto (come risulta evidente dall’uso esplicito di this per la chiamata di

prendiDalTavolo

); in un’esecuzione di

turno

in cui viene chiamato (indirettamente, tramite

sePuoiRuba

)

cediMazzetto

, invece,

turno

e

cediMazzetto

operano su oggetti distinti (nell’ipotesi che il parametro attuale

giocatore

passato a

sePuoiRuba

si riferisca effettivamente a un altro giocatore).

3. usare il pattern “Chain of Responsibility” per creare almeno 4 strategie di gioco

Per prima cosa definiamo opportunamente l’interfaccia

Strategia

. Non c’è molto da inventare, visto che è un tipo già citato in

Giocatore

: dovrà come minimo esporre un metodo

getCard

compatibile con l’uso in

Giocatore.turno

. La “strategia” del resto serve a scegliere una carta fra quelle che si hanno in mano.

public interface SelettoreCarta { Card getCard(List<Card> mano);

}

A questo punto, fra l’altro, non abbiamo più problemi in compilazione, quindi è più facile fare prove.

Possiamo ora implementare le varie strategie “atomiche” che potranno poi essere concatena-te per comporre (via “Chain of Responsibility”) la straconcatena-tegia di ogni giocatore, impostandola

con

Giocatore.setStrategia(SelettoreCarta strategia)

. Ogni strategia, meno l’ultima, avrà quindi un riferimento alla prossima, che potrà usare quando l’approccio seguito fallisce: si noti che non occorre sapere di quale strategia effettivamente si tratti (l’oggetto viene usato, ma non costruito), l’importante è che sia di tipo

SelettoreCarta

e fornisca quindi un metodo

getCard

.

Iniziamo a implementare l’ultima, che è quella che non può fallire: tutte le “catene strategiche” dovranno terminare con questa.

public class StrategiaCartaCasuale implements SelettoreCarta {

@Override

public Card getCard(List<Card> mano) { Random random = new Random();

int i = random.nextInt(mano.size());

return mano.get(i);

} }

Poi implementiamo la strategia che, se c’è, sceglie una carta per prendere una carta sul tavolo. Questa strategia può fallire: quando non c’è una carta adatta alla presa. Bisogna quindi prevedere la conoscenza di una “prossima” strategia.

public class StrategiaCartaTavolo implements SelettoreCarta { private final SelettoreCarta next;

public StrategiaCartaTavolo(SelettoreCarta next) { this.next = next;

}

@Override

public Card getCard(List<Card> mano) { for (Card card : mano) {

if (Partita.TAVOLO.inMostra(card)) return card;

}return next.getCard(mano);

} }

Ora implementiamo la strategia che, se c’è, prende una carta per rubare un mazzetto.

Anche questa strategia può fallire. Inoltre per scegliere

getCard

non può basarsi solo sulle carte in mano (né, come prima, sulle informazioni “globali” delle carte in tavola).

Bisogna quindi fornire informazioni ulteriori (per esempio un riferimento al

Giocatore

) in fase di costruzione della strategia.

public class StrategiaMazzetto implements SelettoreCarta { private final Giocatore giocatore;

private final SelettoreCarta next;

public StrategiaMazzetto(Giocatore g, SelettoreCarta next) { giocatore = g;

this.next = next;

}

@Override

4.4 Soluzioni

public Card getCard(List<Card> mano) { for (Card card : mano) {

Iterator<Giocatore> altri = giocatore.getAltri();

while (altri.hasNext()) { Giocatore g = altri.next();

if (g.getMazzettoTop() == card.getRank()) { return card;

} }

}return next.getCard(mano);

}

L’ultima strategia è molto simile alla precedente (il che dovrebbe suggerire l’opportunità di un refactoring, quando avremo preso confidenza anche con l’ereditarietà).

public class StrategiaProtezione implements SelettoreCarta { private final Giocatore giocatore;

private final SelettoreCarta next;

public StrategiaProtezione(Giocatore g, SelettoreCarta next) { giocatore = g;

this.next = next;

}

@Override

public Card getCard(List<Card> mano) { for (Card card : mano) {

if (card.getRank() == giocatore.getMazzettoTop()){

return card;

} }

return next.getCard(mano);

} }

4. In Rubamazzetto creare i giocatori componendo diverse catene di strategie Giocatore carlo = new Giocatore("Carlo");

Giocatore mattia = new Giocatore("Mattia");

Giocatore violetta = new Giocatore("Violetta");

partecipanti.add(carlo);

partecipanti.add(mattia);

partecipanti.add(violetta);

carlo.setStrategia(new StrategiaProtezione(carlo, new StrategiaCartaCasuale()));

mattia.setStrategia(new StrategiaMazzetto(mattia, new StrategiaCartaCasuale()));

violetta.setStrategia(new StrategiaMazzetto(violetta, new StrategiaProtezione(violetta,

new StrategiaCartaTavolo(new StrategiaCartaCasuale()))));

Ora manca solo il TODO in Partita

.distribuisciMano

. L’implementazione deve

rendere vere le post-condizioni indicate.

public void distribuisciMano(int num){

// PRE CONDIZIONI assert num <= 3;

for (int i=0; i<num && deckSize() >= giocatori.size(); i++) { distribuisciCarta();

}// POST CONDIZIONI

for (Giocatore giocatore : giocatori) {

assert giocatore.numCards() <= 3 : "non si possono avere più di tre carte in mano";

assert giocatori.get(0).numCards() == giocatore.numCards() : "non è stato dato stesso numero di carte a tutti";

assert giocatore.numCards() == 3 || deckSize() < giocatori.size() : "si possono avere meno di tre carte solo se nel mazzo non ce ne sono abbastanza per fare un altro giro";

⤷ ⤶

⤷ } }

5 Lab05: BlackJack

Il punto di partenza di questo laboratorio è disponibile all’indirizzo:

https://gitlab.

com/programmazione2/lab05

. Il codice usa l’implementazione delle carte da gioco di Martin P. Robillard.

5.1 Esercizi

Documenti correlati