Gioco del sette e ½ .
7mezzo
Scarica le immagini delle carte da gioco.
La libreria Swing di java fornisce i componenti per l'interfaccia grafica dotati di proprietà e metodi notevolmente migliorati rispetto alla libreria awt.

La libreria Swing si basa su awt, quindi occorre includerla nella compilazione.
1import java.awt.*;
2import java.awt.event.*;
3import javax.swing.*;

Per aggiungere i nuovi componenti disponibili nella libreria Swing basta anteporre J al tipo del componente. Ad esempio per derivare una classe dalla classe Frame si deve specificare JFrame:
4class MezzoSette extends JFrame implements ActionListener {
La classe MezzoSette eredita le proprietà e i metodi della classe JFrame e ridefinisce (implements) il gestore (ActionListener) dell'evento generato dalla pressione del pulsante.

La classe JFrame presenta alcuni vantaggi rispetto alla classe Frame, ad esempio semplifica la definizione del gestore degli eventi sulla finestra.

Proprietà della classe.
Il livello di visibilità delle proprietà è private, cioè l'accesso a questi campi è consentito solo tramite metodi della classe. Da un'altra classe non è possibile leggere o scrivere direttamente in tali proprietà.
5  private String Carte[] = new String[41];
La proprietà Carte è un array di 41 Stringhe (è compresa anche l'immagine della carta coperta), ognuna delle quali rappresenta il nome dato al file che contiene l'immagine della carta.

6  private int Mazzo[] = new int[41];
L'array Mazzo viene usato per contenere gli indici delle 40 carte, compresa quella coperta che resta memorizzata nella prima posizione.

L'uso di questo secondo array si rende conveniente perchè in esso si memorizzano gli indici delle carte e si possono mescolare gli indici anzichè le stringhe. Inoltre, come si vedrà, si facilita il calcolo del punteggio.

7  private double Punti[] = new double[4];
L'array Punti prevede che si possano mostrare i punteggi di, al massimo, 4 giocatori.

8  private int prosCarta;
La variabile prosCarta conta le carte distribuite e quindi indica la posizione, all'interno dell'array Mazzo, della prossima carta da prelevare.

9  private Point x0y0 = new Point(10, 25);
la variabile x0y0 contiene le coordinate dell'angolo superiore sinistro del riquadro in cui si mostra la prima carta del primo giocatore.

10  private int dxdy = 30;
la proprietà dxdy, invece, è lo spostamento in orizzontale e in verticale del punto in cui si mostra la prossima carta.

11  private int Giocatore = 0;
Inizia il primo giocatore.

12  private boolean terminato = false;
una mano termina quando si sono distribuite le carte a tutti i giocatori.

Componenti dell'interfaccia grafica
13  private JLabel Punteggio[] = new JLabel[4];
Punteggio è un array di 4 Label. All'interno di esse si mostra il punteggio di ciascun giocatore.

14  private JLabel imgCarte[] = new JLabel[44];
imgCarte è un array di 44 Label. All'interno di esse si mostra l'immagine delle carte distribuite ai giocatori. Sono stati riservati 44 elementi all'array per consentire che oltre alle 40 carte distribuite ci siano anche le prime 4 carte coperte.

15  private ButtonGroup AC = new ButtonGroup();
16  private JButton Altra[] = new JButton[4];
17  private JButton Sto[] = new JButton[4];
L'array di pulsanti Altra e Sto vengono inseriti in un gruppo. Questi pulsanti hanno lo scopo di permettere ad un giocatore di chiedere un'altra carta o di dichiarare che non si desiderano altre carte.

18  private JLayeredPane TavoloVerde;
il componente TavoloVerde di classe JLayeredPane raccoglie i componenti che si vogliono disporre sulla finestra, assegnando loro un Layer (strato). Un Layer rappresenta il livello di profondità in cui si vuole posizionare l'immagine. Nel caso specifico le carte verranno mostrate parzialmente sovrapposte.

il Costruttore
Osservando i valori assegnati a ciascun elemento dell'array Carte, si individua una caratteristica che permetterebbe di comporre, tramite un semplice ciclo for, i vari nomi.

Come esercizio provare a sostituire le 40 assegnazioni con un ciclo:
  1. creare un array Seme di 4 elementi, inizializzato con "Coppe", "Denari", "Spade", "Bastoni"
  2. all'interno di un ciclo, in cui la variabile di controllo varia da 0 a 39:
    1. calcolare sia il quoziente che il resto della divisione della variabile di controllo del ciclo per 10
    2. utilizzare questi due valori per assegnare il valore all'elemento dell'array Carte
19  public MezzoSette() {
20    Carte[0] = "coperta";
21    Carte[1] = "ACoppe";
22    Carte[2] = "2Coppe";
23    Carte[3] = "3Coppe";
24    Carte[4] = "4Coppe";
25    Carte[5] = "5Coppe";
26    Carte[6] = "6Coppe";
27    Carte[7] = "7Coppe";
28    Carte[8] = "8Coppe";
29    Carte[9] = "9Coppe";
30    Carte[10] = "10Coppe";
31    Carte[11] = "ADenari";
32    Carte[12] = "2Denari";
33    Carte[13] = "3Denari";
34    Carte[14] = "4Denari";
35    Carte[15] = "5Denari";
36    Carte[16] = "6Denari";
37    Carte[17] = "7Denari";
38    Carte[18] = "8Denari";
39    Carte[19] = "9Denari";
40    Carte[20] = "10Denari";
41    Carte[21] = "ASpade";
42    Carte[22] = "2Spade";
43    Carte[23] = "3Spade";
44    Carte[24] = "4Spade";
45    Carte[25] = "5Spade";
46    Carte[26] = "6Spade";
47    Carte[27] = "7Spade";
48    Carte[28] = "8Spade";
49    Carte[29] = "9Spade";
50    Carte[30] = "10Spade";
51    Carte[31] = "ABastoni";
52    Carte[32] = "2Bastoni";
53    Carte[33] = "3Bastoni";
54    Carte[34] = "4Bastoni";
55    Carte[35] = "5Bastoni";
56    Carte[36] = "6Bastoni";
57    Carte[37] = "7Bastoni";
58    Carte[38] = "8Bastoni";
59    Carte[39] = "9Bastoni";
60    Carte[40] = "10Bastoni";

altre operazioni del costruttore.
61    for (int i = 0; i<40; i++) Mazzo[i] = i+1;
Si inizializza il secondo array, Mazzo, inserendo, in ciascun elemento, gli indici delle carte.

62    Mescola();
Si richiama la procedura Mescola() che agisce sull'array Mazzo, lasciando in ordine i nomi nell'array Carte.

63    for (int i=0; i<44; i++)
64      imgCarte[i] = new JLabel();
Si creano le 44 Label (in cui si mostreranno le immagini delle carte) assegnando il riferimento ad un elemento dell'array imgCarte.

65    prosCarta=0;
La prossima carta da distribuire è quella che si trova nella prima posizione dell'array Mazzo.

66    TavoloVerde = new JLayeredPane();
67    TavoloVerde.setPreferredSize(new Dimension(600, 400));
Si crea un JLayeredPane (un pannello con vari strati) e si assegna il riferimento ad esso alla proprietà TavoloVerde.
Inoltre si definisce la dimensione del pannello.

68    for (int i=0; i<4; i++) {
69      Punteggio[i] = new JLabel("0.0");
70      TavoloVerde.add(Punteggio[i], 1);
71      Punteggio[i].setBounds(5+i*150,250,55,25);
72      Altra[i] = new JButton("Dai");
73      AC.add(Altra[i]);
74      Altra[i].setBounds(5+i*150,0,55,25);
75      TavoloVerde.add(Altra[i],1);
76      Altra[i].setActionCommand("Dai");
77      Altra[i].addActionListener(this);
78      Sto[i] = new JButton("Sto");
79      AC.add(Sto[i]);
80      Sto[i].setBounds(65+i*150,0,55,25);
81      TavoloVerde.add(Sto[i],1);
82      Sto[i].setActionCommand("Sto");
83      Sto[i].addActionListener(this);
84    }
Linea 68 - Per ognuno dei 4 giocatori:
Linea 69 - crea un label con didascalia 0.0, ed assegna il riferimento ad essa all'i-mo elemento dell'array Punteggio
Linea 70 - aggiunge la label al pannello, collocandola al livello di profondità 1.
Linea 71 - le label vengono posizionate sul pannello a distanza orizzontale di 150 pixel l'una dall'altra e a 250 pixel dal bordo superiore.
Linea 72÷75 - si creano i pulsanti per ciascun giocatore aggiungendoli al pannello e posizionandoli all'interno dell'area assegnata a ciascun giocatore.
Linea 76 - quando si preme il pulsante i-mo si invia un messaggio il cui mittente viene identificato con Dai.
Linea 77 - al pulsante si dice quale classe contiene il gestore degli eventi a cui deve inviare i messaggi.
Linea 78÷83: le stesse operazioni compiute sul pulsante Altra si ripetono per l'altro pulsante Sto.

Mostrare le immagini
85    imgCarte[0].setIcon(new ImageIcon("Napoletane/" + Carte[0] + ".jpg"));
86    imgCarte[41].setIcon(new ImageIcon("Napoletane/" + Carte[0] + ".jpg"));
87    imgCarte[42].setIcon(new ImageIcon("Napoletane/" + Carte[0] + ".jpg"));
88    imgCarte[43].setIcon(new ImageIcon("Napoletane/" + Carte[0] + ".jpg"));
Si associano 4 label con l'immagine della carta coperta.
SetIcon è un metodo del componente JLabel. Le immagini si trovano in una cartella denominata "Napoletane". Il nome di un'immagine si trova nell'array Carte

89    TavoloVerde.add(imgCarte[0], new Integer(0));
90    TavoloVerde.add(imgCarte[41], new Integer(0));
91    TavoloVerde.add(imgCarte[42], new Integer(0));
92    TavoloVerde.add(imgCarte[43], new Integer(0));
93    imgCarte[0].setBounds(x0y0.x,x0y0.y,55,101);
94    imgCarte[41].setBounds(x0y0.x+150,x0y0.y,55,101);
95    imgCarte[42].setBounds(x0y0.x+300,x0y0.y,55,101);
96    imgCarte[43].setBounds(x0y0.x+450,x0y0.y,55,101);
Le immagini delle carte vengono aggiunte al pannello (Linee 89÷92) e posizionate (Linee 93÷96) sul pannello in corrispondenza della regione riservata a ciascun giocatore.

97    this.getContentPane().add(TavoloVerde);
La finestra contiene un pannello principale sul quale non si possono aggiungere componenti ma solo pannelli.
L'istruzione getContentPane() restituisce il riferimento al contenitore principale contenuto nella finestra e, tramite questo, richiama il metodo add per aggiungere il pannello JLayered.

98    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Una caratteristica della libreria Swing è che si può associare l'evento chiudi finestra, generato dalla pressione del tasto X senza dover definire una classe "GestoreFinestra".

99    nuovaPartita();
100  }
A questo punto si chiama la funzione nuovaPartita che svolge le operazioni richieste non solo quando si avvia, ma anche quando si deve iniziare una nuova mano.


L'algoritmo per mescolare le carte
101  public void Mescola(){
102    for (int i=39; i>1; i--) {
(Si ricordi che nella posizione 0 dell'array Mazzo c'è la carta coperta)
A iniziare dall'ultima carta (i=39) e togliendo ogni volta una carta (i--) fino a quando resta una sola carta (i>1) svolgi le operazioni seguenti:

103      int x = (int)(Math.random()*i);
Il procedimento per mescolare le carte può essere immaginato come se consistesse nell'estrarre ogni volta una carta dal mazzo e disporle una sull'altra da parte.

La variabile i, di controllo del ciclo, indica il numero di carte non ancora estratte, quindi l'espressione della linea 103 genera un numero a caso x compreso tra 0 e i-1.

104      int tmp = Mazzo[x];
La carta in posizione x del Mazzo viene messa da parte.

105      Mazzo[x] = Mazzo[i];
Al suo posto si inserisce l'ultima carta, quella che si trova in posizione i.

106      Mazzo[i] = tmp;
107    }
108  }
La carta estratta viene messa in ultima posizione.


Iniziare una nuova mano
109  public void nuovaPartita(){
110    for (int i=1; i<4; i++) {
escluso il giocatore numero 0:

111      Altra[i].setEnabled(false);
112      Sto[i].setEnabled(false);
113      Punti[i]=0;
114      Punteggio[i].setText("0");
115    }
Si disabilitano i pulsanti e si mostrano i punteggi 0.

116    Punti[0]=0;
117    Punteggio[0].setText("0");
118  }
Per il giocatore 0 si deve solo mostrare il punteggio 0.

il gestore dell'evento tasto premuto
119  public void actionPerformed(ActionEvent e) {
120    if (terminato) return;
Se la variabile logica terminato è posta a vero il gioco è concluso e si deve solo iniziare una nuova partita. (ma in questo esempio si interrompe semplicemente, la soluzione è lasciata per esercizio.)

121    if ("Dai".equals(e.getActionCommand())) {
Se è stato premuto il tasto con la didascalia Dai, vuol dire che il giocatore ha chiesto un'altra carta.

122      prosCarta++;
123      imgCarte[prosCarta].setIcon(new ImageIcon("Napoletane/" + Carte[Mazzo[prosCarta]] + ".jpg"));
Si avanza di una posizione nell'array Mazzo, si preleva la corrispondente immagine della carta e la si associa alla prossima label

124      TavoloVerde.add(imgCarte[prosCarta], new Integer(prosCarta));
125      imgCarte[prosCarta].setBounds(x0y0.x,x0y0.y,55,101);
La carta viene aggiunta al pannello in uno strato superiore e collocata nelle opportune coordinate.

126      x0y0.x += dxdy;
127      x0y0.y += dxdy;
Si calcola la prossima posizione in cui mostrare la prossima carta.

128      int valore = Mazzo[prosCarta] % 10;
129      if (valore>7 || valore == 0)
130       Punti[Giocatore] += 0.5;
131      else
132       Punti[Giocatore] += valore;
Per calcolare il valore della carta si prende la cifra delle unità dall'indice della carta. Se l'indice è compreso tra 7 e 10, il punteggio del giocatore viene incrementato di 0.5 punti, altrimenti viene incrementato del valore della carta.

133      Punteggio[Giocatore].setText(" " + Punti[Giocatore]);
134      if (Punti[Giocatore]>=8) {
135       Avanza();
136      }
137    }
Si mostra il punteggio del giocatore.

Se è maggiore di 8 si deve passare al giocatore successivo, se c'è, richiamando la funzione Avanza().

138    if ("Sto".equals(e.getActionCommand())) {
139      Avanza();
140    }
141  }
Si deve passare al giocatore successivo anche quando il giocatore preme il pulsante Sto.


Passare al prossimo giocatore
142  public void Avanza() {
143    Sto[Giocatore].setEnabled(false);
144    Altra[Giocatore++].setEnabled(false);
Si disabilitano i tasti del giocatore e si passa al giocatore successivo.

145    if (Giocatore==4) {
146      terminato = true;
147      return;
148    }
Se si è giunti all'ultimo giocatore il gioco termina.

149    Altra[Giocatore].setEnabled(true);
150    Sto[Giocatore].setEnabled(true);
151    x0y0 = new Point(10+Giocatore*150,25);
152  }
153}
altrimenti si abilitano i pulsanti del prossimo giocatore e si riposiziona la variabile che mantiene le coordinate della posizione in cui mostrare la prossima carta.

La classe principale
154class Gioco7m {
155  public static void main(String argv[]) {
156    MezzoSette f = new MezzoSette();
157    f.setTitle("Gioco del 7 e mezzo");
158    f.setSize(600,400);
159    f.setVisible(true);
160  }
161}

Esercizi
  1. risolvere l'esercizio proposto nel costruttore.
  2. inserire i nomi dei giocatori in prossimità di ciascuna carta coperta.
  3. Quando si verifica terminato=true, anzichè uscire, scrivere le istruzioni necessarie per continuare con la mano successiva.
  4. Riconoscere la carta che ha il valore di matta e gestire l'avanzamento del gioco quando questa si presenta.
  5. Ogni carta viene collocata su un nuovo Layer, Quindi l'ultima carta potrebbe trovarsi sul 40-mo layer. Modificare l'istruzione che assegna una carta ad un layer facendo in modo che, ogni volta che si inizia a distribuire le carte ad un giocatore, si ricominci sempre dallo stesso Layer.

Applicazione in formato jar
Per maggiori informazioni si può fare riferimento al manuale java.

Il programma appena costruito, seppure funzionante parzialmente, viene eseguito richiamando l'interprete java da un prompt dei comandi. In questa sezione si mostra il modo in cui produrre un'applicazione eseguibile con un clic sulla sua icona.

Il formato jar (Java ARchive) è simile al formato zip in cui più file sono compressi in un'unico file.

Un file in formato jar può essere reso eseguibile, purchè sia presente la JVM, se nel comando di compressione si specifica anche il nome della classe principale.

Bisogna rispettare alcune regole:
  • ogni classe deve essere dichiarata public e deve essere salvata in un file separato avente lo stesso nome della classe e estensione .java.
  • il metodo main() deve essere dichiarato nella forma:
    public static void main(String[ ] args) {
Bisogna, dapprima, creare un semplice file di testo, denominato, ad esempio, gioco.txt, in cui vi è la sola linea:
Main-Class: Gioco7m
terminata con un vai a capo. Salvare il file nella stessa cartella dei file da compattare.

creare l'applicazione jar con il comando:
jar cmf gioco.txt sette12.jar MezzoSette.class Gioco7m.class Napoletane
Se i file da compattare si trovano tutti nella stessa cartella, allora si può usare l'abbreviazione *.class per includere tutti i fle con estensione .class.

Per una spiegazione del comando vedere alla fine di questa sezione.
Con la versione 6 di java è stata introdotta l'opzione e evita di creare il file Manifest perchè permette di specificare il nome della classe contenente il metodo main direttamente sul comando jar..

Per creare il file in formato jar usare il seguente comando:

jar cfe nome-file.jar Nome-Classe elenco-file.class

jar è il comando;
cfe sono le opzioni: la c specifica che si vuole creare un file JAR, la f specifica che il risultato deve essere inviato ad un file su disco, anzichè sul video, la e (entry point) specifica che nei campi del comando è indicata anche la classe che contiene il metodo main. L'opzione m (usata nell'esempio precedente) specifica che sulla linea del comando c'è il nome del file che indica il nome della classe contenente il main.
nome-file.jar è il nome che si vuole dare al file (compresa l'estensione .jar)
Nome-Classe è il nome della classe che contiene il metodo main.
elenco-file.class sono i nomi di tutte le classi da includere nel file JAR, separati da spazio, compresa l'estensione .class, si devono specificare anche le cartelle contenenti dati usati dall'applicazione.

Ad esempio per ottenere il file in formato jar del programma presentato in questa sezione:
jar cfe Gioco.jar Gioco7m Gioco7m.class MezzoSette.class Napoletane

A questo punto il programma può essere eseguito con un clic sull'icona del file Gioco.jar.