SimCity
Progetto di Programmazione di Febbraio 2014
Il seguente progetto è destinato agli studenti che vogliano sostenere l'esame di Programmazione 2 nel mese di Febbraio. Va realizzato in gruppi di due o tre persone (eccezionalmente saranno ammessi gruppi di una sola persona, previa richiesta di autorizzazione). Se siete interessati a svolgerlo:
Dopo la data di consegna, verrà pubblicato l'elenco dei gruppi ammessi all'orale e verranno indicate le date in cui gli orali avranno luogo.
Dovete realizzare un insieme di classi per simulare gli spostamenti delle persone in un modello semplice ma preciso di città. Si richiede che il programma stampi, dato questo modello, una serie di proprietà statistiche, come ad esempio gli edifici più affollati e le strade più trafficate.
La città viene rappresentata come una serie di edifici e di strade. Una strada collega una coppia di edifici; esistono strade a senso unico e strade a doppio senso, che possono essere percorse indifferentemente in entrambi i sensi.
Le persone poste nella città così creata si muovono in base a regole probabilistiche basate sull'ora del giorno: ogni edificio deve avere infatti una probabilità in ingresso e una probabilità in uscita, entrambe dipendenti dall'ora del giorno e dal numero di persone già presenti nell'edificio. Una persona che si trovi in un certo edificio edificio in cui sono in tutto presenti x persone uscirà dall'edificio all'ora t: Math.random() < edificio.getExitingProbability(t,x)
Se questo viene valutato a false, la persona resterà nell'edificio fino all'ora successiva, e ripeterà il controllo. Se questo viene invece valutato a true, la persona uscirà dall'edificio e dovrà controllare tutte le strade che partono dall'edificio edificio valutando, per ognuna, la probabilità di ingresso nell'edificio che si trova dall'altro lato della strada; in altre parole, dovrà controllare se Math.random() < altroEdificio.getEnteringProbability(t,y) dove y è il numero di persone presenti in altroEdificio.
In caso affermativo, la persona si sposterà nell'altro edificio (percorrendo quindi la strada che li collega). Altrimenti, verrà presa in esame un'altra strada, e così via. Se nessuno dei tentativi ha dato un esito positivo, la persona resterà nell'edificio di partenza per un'altra ora.
Per aiutarvi a scrivere tutto questo, nel paragrafo ArrayList si spiegherà come usare le ArrayList
, che possono rendere più semplice il progetto. Nel paragrafo Classi verranno forniti tutti i dettagli delle classi richieste. Nel paragrafo Caratteristiche aggiuntive verranno esposte delle ulteriori funzionalità che potete implementare, se volete, in un secondo momento. Infine, nel paragrafo Esempio di esecuzione riportiamo un esempio possibile del main e dell'output ottenuto.
Il progetto verrò valutato non solo in base al suo corretto funzionamento, ma anche in base alla pulizia del codice ed alla sua documentazione (si consiglia di scrivere i commenti javadoc
e di commentare il codice).
Le ArrayList
(nel pacchetto java.util
) servono a rappresentare delle liste di oggetti il cui numero non è noto in partenza. Sono quindi simili agli array, ma più flessibili. In particolare, può tornarvi utile creare una ArrayList di Strade e una ArrayList di Edifici. Ecco alcuni esempi d'uso:
ArrayList<Edificio> edifici;
ArrayList<Strada> strade;
edifici = new ArrayList<Edificio>();
strade = new ArrayList<Strada>();
edifici.add(x)
edifici.size()
edifici.get(i)
Per confronto, vediamo come le stesse operazioni vengono fatte sugli array:
Edificio[] edifici;
edifici = new Edificio[N];
edifici.length
edifici[i]
Se preferite, nulla vi vieta di usare gli array Edificio[]
o Strada[]
laddove il progetto richiede una ArrayList<Edificio>
o ArrayList<Strada>
. Questo però potrebbe rendervi il tutto molto più complicato.
Descriviamo ora nei dettagli come realizzare le classi necessarie al progetto. Se lo ritenete, potete apportare cambiamenti alle segnature dei metodi proposti ed anche -- a vostro rischio e pericolo -- alla struttura delle Classi; ovviamente, rimanendo all'interno dei requisiti esposti nel paragrafo introduttivo.
N.B.: Oltre ai metodi qui indicati, ricordatevi di aggiungere ad ogni classe un metodo toString() valido.
Rappresenta uno degli edifici della città. Sotto forniamo alcuni esempi di classi che implementano questa interfaccia.
double getProbabilitaEntrata(int ora, int persone)
: deve restituire la probabilità (double compreso tra 0 e 1) di entrare nell'Edificio all'ora ora (un intero compreso fra 0 e 23) supponendo che nell'edificio ci siano persone persone presenti.double getProbabilitaUscita(int ora, int persone)
: deve restituire la probabilità di uscire dall'edificio.Ad ogni strada sono associate due estremità (i due edifici che la strada collega bidirezionalmente) e un intero (il numero di persone che sono fino ad ora transitate lungo quella strada).
Strada(Edificio a, Edificio b, boolean sensoUnico)
: costruisce un oggetto strada che collega a e b, e che è o meno un senso unico a seconda del valore dell'argomento booleano.
Strada(Edificio a, Edificio b)
: costruisce un oggetto strada che collega a e b a doppio senso di percorrenza.
Edificio getDestinazioneDa(Edificio x)
: se l'edificio x è una delle due estremità di questa strada, restituisce l'edificio posto all'altro capo. Se x non è una delle due estremità, solleva una NoEndException. Se x è una delle due estremità, ma la strada è un senso unico e x è il punto di arrivo, solleva una OneWayException. Entrambe le eccezioni sono di tipo controllato.
boolean isCollegamentoDa(Edificio x)
: restituisce true sse x è una estremità di partenza per questa strada.
boolean isCollegamentoA(Edificio x)
: restituisce true sse x è una estremità di arrivo per questa strada.
boolean isCollegamento(Edificio x)
: restituisce true sse x è una estremità di questa strada.
void segnaPedaggio()
: aumenta di uno il numero totale di passaggi su questa strada.
int getNumeroPedaggi()
: restituisce il numero totale di passaggi su questa strada.
Una città è un insieme di edifici e di strade. Inoltre contiene dentro di sé un orario (segnato dalla torre dell'orologio): alla creazione tale orario è 0 (mezzanotte), e viene incrementato ogni volta che viene chiamato il metodo passaOra()
.
static boolean isOraDiurna(int ora)
: restituisce true sse ora è compresa tra 8 (alba) e 19 (tramonto).
void addEdificio(Edificio e)
: aggiunge l'edificio e alla città.
void creaStrada(Edificio a, Edificio b, boolean sensoUnico)
: aggiunge alla città una nuova strada che collega a e b.
void creaStrada(Edificio a, Edificio b)
: aggiunge alla città una nuova strada a doppio senso di percorrenza che collega a e b.
ArrayList<Edificio> getEdifici()
: resituisce tutti gli edifici di questa città.
ArrayList<Strada> getStrade()
: resituisce tutte le strade di questa città.
ArrayList<Strada> getStradeDa(Edificio x)
: restituisce tutte le strade che comprendono l'edificio x.
void passaOra()
: incrementa l'ora sull'orologio della torre (dopo le ore 23 viene la mezzanotte, indicata da 0).
int getOra()
: fornisce l'ora attuale.
Una persona è caratterizzata da un nome, da una città in cui vive, da un edificio in cui si trova inizialmente.
Persona(String nome, Citta citta, Edificio iniziale)
: crea una persona di nome nome, che viaggia nella città citta, partendo dall'edificio iniziale.
Edificio getDoveSei()
: restituisce l'edificio in cui si trova questa persona.
void next()
: la persona cambia edificio, operando nel seguente modo:
getOra()
di Citta);true
;getStradeDa
della classe Citta
), e per ognuna prova ad entrare nell'edificio cui si giunge prendendo quella strada; ciò avviene se Math.random() < altroEdificio.getEnteringProbability(t) restituisce true
; il primo edificio per cui ciò avviene è quello in cui la persona andrà;Si tratta di una classe che permette di simulare gli spostamenti di un certo numero di persone in una città.
Simulazione(Citta citta, int nPersone, int nStep, Edificio partenza)
: crea una Simulazione, costituita da nPersone Persone, che si muovono per nStep ore all'interno del modello di città citta. Queste persone vengono tutte inizializzate nell'Edificio partenza. La simulazione viene qui solo inizializzata. Le persone possono essere tenute in un array.
void run()
: esegue effettivamente i passi della simulazione. Ad ogni passo, ogni persona viene spostata (con il suo metodo next()
).
Strada trovaStradaPiuTrafficata()
: trova la Strada con il maggiore numero di pedaggi.
void printTrafficoStrade(int trafficoMinimo)
: stampa con System.out.println
delle righe del tipo
Strada<Case,Pub> -> 154 pedaggi
per tutte le strade con più di trafficoMinimo pedaggi.
void printStatisticheLuoghi()
: stampa, per ogni istante simulato, il luogo più affollato in quel momento. Le righe stampate devono quindi assomigliare a questa:
17:00 (giorno 3) -> Buy'N'Large Corp. (79 persone)
Per realizzare questo metodo occorre, per ogni edificio, contare quante persone si trovano in quell'edificio (usando il metodo getDoveSei()
di persona e contando per quante persone tale metodo restituisce l'edificio in esame). Si ripete questo per ogni edificio tenendo traccia del massimo.
Rappresenta un edificio in cui possono lavorare delle persone. Quando viene creato gli viene assegnato un nome, un orario di inizio dell'attività (per esempio, 8), un orario di fine dell'attività (per esempio, 18) e un'importanza (un valore fra 0 e 1). La probabilità di entrata è pari all'importanza durante l'orario di lavoro, ed è 0 altrimenti. La probabilità di uscita è 0 durante l'orario di lavoro e 1 fuori da tale orario.
Questa classe rappresenta il "quartiere residenziale" della città, ovvero dove abitano i suoi abitanti. La probabilità di ingresso è 0.05 fra le 8:00 e le 18:00, ed è 0.90 altrimenti. La probabilità di uscita è 0 nelle ore notturne e 0.90 nelle ore diurne (vedi metodo isOraDiurna
di Citta). Notate che si assume che esista una sola istanza di questa classe, che rappresenta l'intero insieme delle case esistenti nella città.
Questa è una classe astratta che rappresenta le sedi di attività pubbliche. Una SedeAttivita è caratterizzata da un nome e da un'importanza (un valore fra 0 e 1). Ogni sottoclasse concreta fornisce un metodo
* abstract boolean isAperta(int ora)
che dice se quell'attività è aperta a una data ora. La probabilità di ingresso è 0 quando l'attività non è aperta, ed è pari all'importanza quando è aperta. La probabilità di uscita è 1 quando l'attività non è aperta, ed è 0.3 quando è aperta.
Sono tutte sottoclassi concrete di SedeAttivita, ognuna delle quali fornisce un'opportuna implementazione del metodo isAperta.
Questa è una classe di edifici con una probabilità di ingresso pari a 0.3 dalle 8:00 alle 13:00, e 0 fuori da questi orari. La probabilità di uscita è 0 dalle 8:00 alle 13:00, e 1 fuori da questi orari.
Questa è una classe di edifici con una probabilità di ingresso pari a 0.05 nelle ore diurne, e 0.01 nelle ore notturne. La probabilità di uscita è 0.9 a qualunque ora.
Arricchite il vostro progetto aggiungendo altre funzionalità (seguono alcuni esempi). Per ogni funzionalità aggiuntiva fate in modo di fornire un'adeguata documentazione (vedi quanto detto sopra nelle modalità di consegna).
Case
nella stessa città. Citta citta = new Citta();
// crea la citta'
Edificio casa = new Case();
citta.addEdificio(new Ospedale());
citta.addEdificio(new PostoDiLavoro("Software House Ltd.", 0.5));
citta.addEdificio(new PostoDiLavoro("Buy'N'Large Corp.", 0.9));
citta.addEdificio(casa);
citta.addEdificio(new Scuola("Liceo Scientifico"));
citta.addEdificio(new Scuola("Medie"));
citta.addEdificio(new Scuola("Istituto Tecnico"));
citta.addEdificio(new Pub());
citta.addEdificio(new CentroCommerciale());
citta.addEdificio(new Discoclub());
// crea le strade
for (Edificio a : citta.getEdifici())
for (Edificio b : citta.getEdifici())
if (!a.equals(b))
if (!(a instanceof PostoDiLavoro && b instanceof PostoDiLavoro))
citta.creaStrada(a, b);
// esegue la simulazione
Simulazione sim = new Simulazione(citta, 100, 96, casa);
sim.run();
// stampa i dati
sim.printStatisticheLuoghi();
sim.printTrafficoStrade(50);
Strada piuTrafficata = sim.trovaStradaPiuTrafficata();
System.out.println("La strada piu' trafficata e' " + piuTrafficata + " (" + piuTrafficata.getNumeroPedaggi() + " pedaggi)");
===============ESECUZIONE=================
0:00 (giorno 0)...
1:00 (giorno 0)...
2:00 (giorno 0)...
[…]
=====LUOGHI PIU' AFFOLLATI ORA PER ORA=====
0:00 (giorno 0)-> Case (100)
1:00 (giorno 0)-> Case (100)
2:00 (giorno 0)-> Case (100)
3:00 (giorno 0)-> Case (100)
4:00 (giorno 0)-> Case (100)
5:00 (giorno 0)-> Case (100)
6:00 (giorno 0)-> Case (100)
7:00 (giorno 0)-> Case (92)
8:00 (giorno 0)-> Case (79)
9:00 (giorno 0)-> Buy'N'Large Corp. (30)
10:00 (giorno 0)-> Buy'N'Large Corp. (31)
11:00 (giorno 0)-> Buy'N'Large Corp. (32)
12:00 (giorno 0)-> Buy'N'Large Corp. (32)
13:00 (giorno 0)-> Buy'N'Large Corp. (65)
14:00 (giorno 0)-> Buy'N'Large Corp. (65)
15:00 (giorno 0)-> Buy'N'Large Corp. (65)
16:00 (giorno 0)-> Buy'N'Large Corp. (65)
17:00 (giorno 0)-> Buy'N'Large Corp. (65)
18:00 (giorno 0)-> Case (93)
19:00 (giorno 0)-> Case (87)
20:00 (giorno 0)-> Case (83)
21:00 (giorno 0)-> Case (78)
22:00 (giorno 0)-> Case (74)
23:00 (giorno 0)-> Case (77)
0:00 (giorno 1)-> Case (81)
1:00 (giorno 1)-> Case (96)
[…]
============= TRAFFICO STRADE =============
Strada<Software House Ltd.,Case> -> 87 pedaggi
Strada<Buy'N'Large Corp.,Case> -> 202 pedaggi
Strada<Case,Software House Ltd.> -> 95 pedaggi
Strada<Case,Buy'N'Large Corp.> -> 194 pedaggi
Strada<Case,Pub> -> 154 pedaggi
Strada<Pub,Case> -> 135 pedaggi
===========================================
La strada piu' trafficata e' Strada<Buy'N'Large Corp.,Case> (202 pedaggi)