Le variabili.
Per la descrizione del procedimento di costruzione del calendario degli incontri tra le squadre, si assuma, per discutere dapprima un caso pratico, che al torneo siano iscritte 8 squadre.

Quindi il torneo si disputa in 7 giornate, in ognuna delle quali si svolgono 4 incontri.

Si abbiano quindi le seguenti variabili:
  • NrSquadre - indica il numero di squadre
  • NrGiornate - è un valore ottenuto sottraendo 1 da NrSquadre
  • NrIncontri - è un valore ottenuto dividendo NrSquadre per 2
L'elenco delle squadre sia memorizzato in un array NomeSquadra

Il risultato verrà prodotto in una matrice Calendario (tridimensionale) di NrIncontri righe, NrGiornate colonne e profonda 2 caselle, in cui verranno scritte le due squadre di un incontro.


Modello di simulazione
Ogni squadra viene individuata con un numero da 1 a 8, e, all'interno dell'array, occupa una posizione compresa tra 0 e 7.

L'algoritmo di Berger usa il modello seguente:
dispone tutti i numeri delle squadre, escluso l'ultima, su un disco (Figura 1). I numeri siano scritti, ad esempio, in verso antiorario.
modello disco
Fig 1: La numerazione sul disco.

Modello di simulazione
Sul disco si pone una maschera con 3 finestre (Fig. 2) che lasciano scoperte 3 coppie di numeri allineati.

Nella finestra superiore si leggono i numeri: 2 e 7. In quella centrale i numeri 3 e 6. Nell'ultima si leggono i numeri 4 e 5.

Queste coppie di numeri allineati rappresentano gli incontri della prima giornata. La squadra che non rientra in nessuna finestra (la Nr. 1) incontra la squadra Nr. 8.

Quindi gli incontri della prima giornata sono:
1 - 8
2 - 7
3 - 6
4 - 5
modello dischi
Figura 2: Le finestre sul disco

Modello di simulazione
Per determinare gli incontri della seconda giornata, si provochi una rotazione in senso orario del disco, in modo che i numeri si spostino di una posizione.

La squadra identificata con il numero esterno alla finestra (la Nr. 2) viene ancora messa in corrispondenza con la squadra esclusa, la Nr. 8.

Leggendo i numeri allineati sul disco (Fig. 3) si trovano gli incontri della seconda giornata:

2 - 8
3 - 1
4 - 7
5 - 6

Continuando a ruotare il disco si determinano gli altri incontri.
modello dischi
Figura 3: il nuovo allineamento.

Modello di simulazione
Il calendario degli incontri che si ottiene applicando l'algoritmo di Berger è il seguente:
Nr. Incontro I II III IV V VI VII
1 1 - 8 2 - 8 3 - 8 4 - 8 5 - 8 6 - 8 7 - 8
2 2 - 7 3 - 1 4 - 2 5 - 3 6 - 4 7 - 5 1 - 6
3 3 - 6 4 - 7 5 - 1 6 - 2 7 - 3 1 - 4 2 - 5
4 4 - 5 5 - 6 6 - 7 7 - 1 1 - 2 2 - 3 3 - 4
Si nota imediatamente che l'algoritmo trascura di alternare le partite (in casa ed in trasferta).

Osservando il calendario si nota che ci sono vari modi per generarlo. Uno di questi ha le seguenti caratteristiche.
  • Il primo incontro di ogni giornata è disputato tra la squadra che ha lo stesso numero della giornata e la squadra che ha l'ultimo numero.
  • Per ogni giornata, la prima squadra degli incontri forma una numerazione crescente ciclica
  • Per ogni giornata, la seconda squadra degli incontri prosegue ciclicamente la numerazione raggiunta con la prima squadra, ma dall'ultimo incontro verso il secondo.

Il programma: La classe con i Gestori degli eventi sulla finestra
1 import java.io.*;
2 import java.awt.*;
3 import java.awt.event.*;
4 class GestoreFinestra implements WindowListener {
5     public void windowIconified(WindowEvent e) {}
6     public void windowDeiconified(WindowEvent e){}
7     public void windowActivated(WindowEvent e){}
8     public void windowDeactivated(WindowEvent e){}
9     public void windowOpened(WindowEvent e){}
10     public void windowClosed(WindowEvent e){}
11    public void windowClosing(WindowEvent e){
12         System.exit(0);
13    }
14 }
La classe GestoreFinestra ridefinisce il gestore dell'evento chiudi Finestra

Proprietà della classe che sviluppa il calendario degli incontri.
classe Torneo
Le proprietà della classe sono:
  1. il numero di squadre (NrSquadre) partecipanti al torneo
    (le altre due proprietà: NrIncontri e NrGiornate si possono ricavare da questa).
  2. il calendario degli incontri, memorizzato in un array tridimensionale: Calendario.
  3. Un pannello
  4. ed una Text Area, da collocare sulla finestra, per mostrare il calendario.
    Un componente Text Area differisce da una casella di testo, perchè permette di disporre il testo su più linee, seperandole con i "vai a capo"

il Costruttore.
classe Torneo
linea 6: Il costruttore viene richiamato passandogli, come parametro, il numero di squadre.

Linee 7÷9: il numero ricevuto viene usato per assegnare i valori alle proprietà della classe.

Linea 10:la casella Text Area viene aggiunta al pannello p

Linea 11: Il pannello viene aggiunto alla finestra;

Linea 12: viene creato un oggetto GestoreFinestra a cui si passano, per essere gestiti, i messaggi generati dagli eventi che si verificano sulla finestra.

Linea 13: Si impedisce di modificare il testo all'interno della Text Area.

il Costruttore (continuazione).
classe Torneo
Linea 14: si crea una variabile locale che conterrà.

Linea 15: Si inizia un ciclo usando la variabile n come numero della giornata,

Linea 16: Si assegna alla variabile locale Squadra lo stesso numero della giornata,

Linea 17: Si inizia un nuovo ciclo in cui la variabile part conta gli incontri della giornata,

Linea 18: La prima squadra di ciascun incontro viene calcolata incrementando la variabile Squadra in modo circolare, cioè dopo l'ultima squadra si ricomincia dalla prima. Notare gli indici dell'array Calendario: la variabile del ciclo esterno conta le giornate, la variabile del ciclo interno conta le partite,

Linea 19: termina il ciclo interno,

Linea 20: Inizia un secondo ciclo nidificato, in cui, questa volta, il conteggio inizia dall'ultimo incontro e prosegue verso il secondo,

Linea 21: La variabile Squadra viene ancora incrementata in senso circolare, continuando dall'ultimo valore raggiunto nel ciclo precedente. I valori ottenuti rappresentano la seconda squadra di ciascuno degli incontri.

Linea 22: Termina il ciclo interno.

Linea 23: La squadra con il numero più elevato è la seconda squadra di tutti i primi incontri.

Line4 24: fine del ciclo esterno.

Linea 25: Fine del costruttore.

Lettura dei risultati
La classe deve contenere un metodo per mostrare, nella Text Area, i risultati. classe Torneo
Linea 27: si dichiarano due variabili locali che avranno lo scopo di contare le giornate e gli incontri di ciascuna giornata.

Linea 28: Si scrive l'intestazione nella Text Area.

Linea 29: Si inizia un ciclo per contare il numero delle giornate.

Linea 30: si scrive il numero della giornata nella Text Area. Notare che si usa il metodo append del componente Text area per non cancellare il testo già contenuto.

Linee 31÷33: si scrivono le coppie di indici delle squadre di ciascun incontro.

Linea 34: Al termine del ciclo di una giornata si stampa una linea di asterischi.

La classe principale
classe Torneo
Linea 40: si crea un'istanza t della classe Torneo, comunicando che ci sono 8 squadre. L'operatore new, per creare l'istanza della classe chiama il costruttore che, dopo aver assegnato i valori iniziali alle proprietà della classe applica l'algoritmo di Berger per determinare il calendario.

Linea 41: A questo punto quindi si può leggere il calendario.

Linea 42: pack ha lo scopo di collocare i componenti sulla finestra occupando il minimo spazio possibile.

Problemi
  1. Riscrivere l'algoritmo di Berger utilizzando l'altro metodo: osservando la tabella riepilogativa) si nota che a iniziare dal secondo incontro, per tutte le giornate, sia per la prima squadra che per la seconda squadra gli indici seguono una numerazione crescente ciclica.
  2. Assegnare i nomi alle squadre e mostrare le squadre anzichè i loro indici.

Completamento
Nella seconda parte si mostra:
- una possibile tecnica di inserimento dei nomi delle squadre;
- il gestore degli eventi generati dalla tastiera;
- una classe incorporata in un'altra.

* * * * * * SECONDA PARTE * * * * * *

Il gestore degli eventi sulla finestra
Le direttive che specificano le librerie da includere nella compilazione e la classe Gestore Finestra:
1 import java.io.*;
2 import java.awt.*;
3 import java.awt.event.*;
4 class GestoreFinestra implements WindowListener {
5     public void windowIconified(WindowEvent e) {}
6     public void windowDeiconified(WindowEvent e){}
7     public void windowActivated(WindowEvent e){}
8     public void windowDeactivated(WindowEvent e){}
9     public void windowOpened(WindowEvent e){}
10     public void windowClosed(WindowEvent e){}
11    public void windowClosing(WindowEvent e){
12         System.exit(0);
13    }
14 }

La classe Squadre
TextArea
Il metodo che si vuole proporre consiste nel fornire una casella di testo multilinea (Text Area) in cui inserire, separandoli con i vai a capo, i nomi delle squadre.

Durante l'immissione si conta il numero di volte che si preme il tasto invio, questo numero rappresenterà il numero di squadre inserite. Questo conteggio viene visualizzato ad ogni pressione del tasto Invio.

Un pulsante consente di segnalare la fine dell'immissione.

A questo punto le squadre elencate nella Text Area vengono separate e, ognuna di esse, viene inserita in un elemento di un array.

Solo allo scopo di verificare il funzionamento del programma, ad ogni pressione del tasto "Fine", si mostra una delle squadre.

I risultati saranno sbagliati se, prima di premere il tasto Fine, si cancellano dei vai a capo. è consentito apportare correzioni purchè non si cancellino i vai a capo.

La dichiarazione della classe:
1 class Squadre extends Frame implements KeyListener, ActionListener {
L'applicazione è ospitata in una finestra (extends Frame) e ridefinisce (implements) i metodi di gestione degli eventi generati dalla tastiera (KeyListener) e quello generato dal pulsante (ActionListener).

Le proprietà della classe
2   private int NrSquadre;
3   private String NomiSq[] = new String[16];
Linea 2: l'intero NrSquadre memorizza il numero delle squadre;

Linea 3: viene creato l'array NomiSq di 30 Stringhe di caratteri. Si possono quindi memorizzare fino a 30 nomi.

4   private Panel p = new Panel();
5   private TextArea risultati = new TextArea(15,30);
6   public Label Righe = new Label("0 Squadre");
7   private Button Pulsante = new Button("Fine");
Le altre proprietà della classe comprendono i componenti presenti sulla finestra.

Linea 4: Si crea un pannello e il riferimento ad esso viene scritto nella variabile p;

Linea 5: viene creata una casella di testo multilinea, denominata risultati, formata da 15 righe di 30 colonne.

Linea 6: si crea una Label allo scopo di fornire un componente su cui scrivere i messaggi prodotti dal programma;

Linea 7: si crea anche un pulsante di comando sul quale appare la scritta "Fine", allo scopo di permettere di segnalare il termine dell'operazione di inserimento dei nomi.

Ridefinizione dei metodi della classe KeyListener
La classe KeyListener richiede che vengano ridefiniti i tre metodi:
  • KeyPressed - generalmente usato per vedere se si preme un tasto particolare (es: F1, Ins, ecc);
8   public void keyPressed(KeyEvent e) {}
In questo caso il metodo keyPressed è vuoto.
  • KeyReleased - il tasto viene rilasciato;
9   public void keyReleased(KeyEvent e) {
10     if (e.getKeyCode() == KeyEvent.VK_ENTER) {
11       risultati.append("-");
12       ++NrSquadre;
13       Righe.setText(NrSquadre + " Squadre");
14     }
15   }
Linea 9: Il gestore dell'evento tasto rilasciato riceve un oggetto e di classe KeyEvent

Linea 10: Ogni volta che si rilascia un tasto, si legge, tramite l'oggetto e ricevuto come parametro, il codice del tasto e lo si confronta con il codice del tasto "Invio".

Se il confronto fornisce esito vero:
si aggiunge un carattere 'trattino' nella casella multilinea, (lo scopo sarà chiarito in seguito).
si conta una squadra,
Nella label si aggiorna l'indicazione relativa al numero di squadre inserite.
Quesito
Compattare il codice del metodo keyReleased
integrando l'istruzione della linea 12 nell'istruzione della linea 13.
  • KeyTyped - il tasto digitato (corrisponde alla pressione seguita dal rilascio). Si usa quando il tasto può essere digitato anche come sequenza di caratteri sulla tastiera numerica. Per questo evento si possono applicare le funzioni getKeyChar() e getKeyCode() per interrogare il carattere o il codice del tasto premuto.
16 public void keyTyped(KeyEvent e) { }
Anche il corpo di questo gestore di evento è vuoto.

il Costruttore
La dichiarazione del costruttore:
17   public Squadre() {
L'assegnazione dei valori iniziali alle proprietà:
18     NrSquadre = 0;
I tre componenti dell'interfaccia grafica (TextArea, Pulsante e Label) vengono aggiunti al pannello:
19     p.add(risultati);
20     p.add(Pulsante);
21     p.add(Righe);
Viene associata la classe contenente il gestore di evento a ciascun componente interessato:
22     risultati.addKeyListener(this);
23    addWindowListener(new GestoreFinestra());
24     Pulsante.addActionListener(this);
Linea 22: gli eventi generati dalla tastiera sulla TextArea vengono inviati a questa (this) classe per essere gestiti.

Linea 23: si crea un oggetto di classe GestoreFinestra e il suo riferimento viene usato per informare la classe di quale deve essere la destinazione dei messaggi generati dagli eventi sulla finestra (Frame)

Linea 24: Il pulsante viene informato che il gestore dell'evento tasto premuto si trva in questa classe.

Infine:
25     add(p);
26     risultati.setEditable(true);
27   }
il costruttore termina le sue operazioni aggiungendo il pannello alla finestra e abilitando l'inserimento nella casella di testo multilinea.

Il gestore dell'evento tasto premuto
28   public void actionPerformed(ActionEvent e) {
29     NomiSq = risultati.getText().split("-");

Linea 29: si applica il metodo getText alla TextArea, ottenendo quindi la stringa completa conteuta nella casella. Su questa stringa si applica, poi, il metodo split

Il metodo split scandice la stringa di testo creando un array con tutti gli elementi che sono separati dal carattere trattino ("-").

Il risultato viene memorizzato nell'array NomiSq.

Il pulsante viene usato anche per uno scopo di verifica dei risultati: Ogni volta che viene premuto si mostra una squadra presente nell'array, fino a quando si arriva alla prima.
30     if (NrSquadre>0) {
31       Righe.setText(NomiSq[--NrSquadre]);
32     }
33   }
34 }

La classe con il metodo main()
35 class ElencoSq {
36   public static void main(String[] args) {
37     Squadre t = new Squadre();
38     t.pack();
39     t.setVisible(true);
40   }
41 }

* * * * * * * * Terza Parte * * * * * * * *

Inserimento Squadre e sviluppo del Calendario degli incontri
In questo programma la classe per acquisire i nomi delle squadre viene incorporata nella classe che applica l'algoritmo di Berger e mostra gli incontri, però, questa volta, utilizzando i nomi delle squadre anziché gli indici.

L'intestazione che specifica le librerie da includere nella compilazione rimane uguale alla precedente, così come resta uguale anche la classe GestoreFinestra.

Bisogna modificare la dichiarazione della proprietà NomiSq:
private static String NomiSq[] = new String[16];
Una proprietà che possiede l'attributo static esiste in una sola locazione ed è usata da tutte le istanze della classe, mentre una proprietà non-static viene creata per ogni istanza della classe. Nel caso specifico, se si creano due oggetti di classe Squadre la proprietà NrSquadre viene creata per ciascun oggetto, mentre la proprietà NomiSq è unica ed è comune ad entrambe le classi.

Bisogna modificare il gestore dell'evento tasto premuto perchè al termine dell'inserimento dei nomi delle squadre la sezione che mostra i singoli elementi dell'array non serve più e si deve eliminare.

Le operazioni richieste, quando si preme il tasto di fine Immissione, sono:
Linea 2: separazione dei nomi delle squadre inserite nella TextArea nei singoli elementi di un array,
Linea 3: nascondere la TextArea,
Linea 4: eliminare l'ascoltatore degli eventi generati dalla tastiera associato alla TextArea.
Linea 5: creare un'istanza della classe Torneo e poi (linea 6) mostrare i risultati forniti dall'algoritmo di Berger.

Metodi della classe Squadre
Non solo le proprietà, ma anche i metodi possono essere static. Un metodo static appartiene alla classe, quindi può essere richiamato specificando la classe anzichè l'istanza.

La classe leggiSquadra è necessaria perchè la proprietà NomiSq è private, cioè è accessibile solo dall'interno della classe. Quindi una classe esterna che vuole leggere l'array deve chiedere ad un metodo della classe di leggere l'array.

Di conseguenza si è reso necessario modificare in static la proprietà NomiSq, perchè non è consentito ad una funzione static di accedere ad un membro non-static della classe. A questo punto termina la dichiarazione della classe Squadre.

Sviluppo del Calendario degli incontri

La prima operazione da compiere all'interno del costruttore (della classe Torneo) è la creazione di un'istanza della classe Squadre

Modifica al metodo di lettura del Calendario


Per mostrare i nomi delle squadre, anzichè gli indici, si richiama la funzione leggiSquadra usando come indice di accesso all'array il valore contenuto nell'array Calendario.

La classe con il metodo main

La classe principale crea un'istanza della classe Squadre perchè sarà questa che poi creerà un'istanza della classe Torneo