Paolo Boldi

Space Invaders [Testo del progetto di Programmazione II per Febbraio 2013]

Indice

Descrizione

Il progetto consiste nell'implementare in Java un'applicazione che simuli, con le dovute semplificazioni, l'esecuzione di una partita al famoso arcade Space Invaders (1978).

Space Invaders

Il Gioco

Nella versione originale del gioco, il giocatore controlla gli spostamenti di un cannoncino laser che può muoversi esclusivamente da destra a sinistra e viceversa nella parte bassa dello schermo mentre dall'alto scendono schiere di alieni desiderosi di conquistare la Terra. Compito del giocatore è proprio quello di distruggere le armate nemiche senza farsi distruggere o lasciare conquistare la Terra.

L'intera armata di alieni si muove sistematicamente da destra verso sinistra fino a raggiungere il bordo estremo quindi scende di una riga e si muove nella direzione opposta e così via fino a toccare terra. Durante i loro spostamenti, gli alieni sparano, in modo asincrono, dei proiettili diretti verso terra. Il cannoncino può sparare alla volta degli alieni e può proteggersi nascondendosi dietro delle casematte (3 per la precisione). Le casematte se colpite un numero adeguato di volte vengono distrutte, mentre basta un singolo colpo a distruggere un alieno o il cannoncino. Gli spari del cannoncino e quelli degli alieni si elidono quando si scontrano. Si hanno a disposizione tre cannoncini mentre gli alieni eliminati non vengono rimpiazzati; se si distrugge l'intera armata un'altra la sostituisce passando al quadro successivo.

Parallelamente al gioco, una navicella aliena appare di tanto in tanto in cima allo schermo muovendosi da sinistra a destra; arrivata al bordo opposto dello schermo sparisce. Ovviamente può essere colpita e rappresenta un bonus.

Semplificazioni e varianti

Ovviamente non è possibile, nell'ambito del progetto, implementare interamente il gioco stesso ma dovrete realizzarne una simulazione che permetta il passaggio da una configurazione ad un'altra. In particolare, imporremo le seguenti limitazioni/varianti:

Lo scopo a questo punto diventa quello di far muovere le varie entità in gioco e di salvare (a richiesta) la configurazione corrente.

Valutazione

Il vostro programma sarà sottoposto a un test automatico che attribuirà un punteggio, da 0 a 30, sulla base di quanti dei metodi e costruttori elencati risulti implementato correttamente. Un punteggio inferiore a 15 comporterà un'esclusione dalla valutazione successiva. La valutazione del progetto verrà poi effettuata, per i soli progetti che abbiano superato la prima fase, tenendo conto dei seguenti criteri:

Consegna

Per consegnare:
Vi ricordo che la documentazione delle classi standard è disponibile puntando il browser su file:///usr/share/javadoc/java/index.html

Classi da Realizzare

È obbligatorio realizzare in Java il programma descritto nelle sezioni precedenti utilizzando le classi sotto indicate. Si noti che è richiesto di:

BattleFieldElement

BattleFieldElement è una classe astratta usata per descrivere il comportamento, in astratto, dei vari personaggi in gioco. Attenzione! Questa classe e tutte quelle che la estendono tranne Gun e Shot hanno un costruttore che riceve la posizione (x, y) dell'elemento e le dimensioni (righe, colonne) del campo di battaglia.

Attributi

Notare la peculiare presenza di alcuni attributi nella classe astratta; vale sempre il principio che lo stato degli oggetti deve essere nascosto ma in questo caso deve poter essere usato dalle classi concrete che estenderanno la classe astratta.

Costanti

Nella classe sono definite quattro costanti (campi statici finali) intere che verranno usate nel progetto per rappresentare le quattro direzioni: SU, GIU, DESTRA, SINISTRA. I loro valori sono arbitrari.

Metodi

La classe BattleFieldElement dovrà essere estesa da diverse altri classi che rappresenteranno i vari elementi del gioco. Nel seguito descriveremo le singole classi senza entrare nel merito dell'implementazione dei metodi ereditati ma solo del comportamento delle sue istanze.

Alien

Alien classe astratta derivata da BattleFieldElement, rappresenta la classe progenitrice di tutte le entità aliene presenti nel gioco.

Attributi

Metodi

RedSpacecraft

RedSpacecraft, classe concreta che estende Alien, rappresenta la navicella bonus; questa si muove sempre e solo da destra verso sinistra di una casella alla volta sulla riga più in alto dello schermo (sollevare l'eccezione IllegalPositionException se si tenta di posizionarla in una riga diversa). Arrivata in fondo all'estrema sinistra sparisce dal campo di gioco.

OneStepAlien

OneStepAlien, classe concreta che estende Alien, rappresenta gli alieni; tutti gli alieni di questo tipo si muovono come se fossero un'unica entità da destra a sinistra e viceversa (non scendono o salgono mai, partono muovendosi da destra verso sinistra). Quando un alieno raggiunge il bordo l'intera armata cambia direzione. L'alieno, in quanto tale, si comporta come se non si potesse più muovere quando raggiunge il bordo.

Shot

La classe Shot è una classe astratta derivata da BattleFieldElement che rappresenta la classe progenitrice di tutti i possibili spari, sia degli alieni (istanze della classe AlienShot) che del cannoncino (istanze della classe GunShot). Due spari che collidono si elidono come pure si elidono lo sparo e l'elemento (alieno, casamatta o cannoncino) che colpisce. Gli spari degli alieni vanno dall'alto verso il basso mentre quelli del cannoncino dal basso verso l'alto. Raggiunti gli estremi del campo di gioco escono dal gioco.

Attributi

Empty

La classe Empty estende BattleFieldElement e rappresenta le caselle vuote che ovviamente non si spostano (o meglio non ha senso che si spostino).

Gun

Le istanze della classe Gun (che estende BattleFieldElement) rappresentano il cannoncino. Quando un cannoncino viene distrutto (colpito da uno sparo o scontrandosi con un alieno) viene rimpiazzato da un altro cannoncino la cui posizione iniziale sarà il centro della linea più in basso del campo di gioco. Ad ogni mossa, il cannoncino procede, una casella alla volta, nella direzione in cui si stava muovendo fino a raggiungere l'estremo del campo di gioco a questo punto inverte la direzione e continua la sua marcia (inizia muovendosi da sinistra a destra). I cannoncini possono essere solo sull'ultima riga altrimenti si deve sollevare l'eccezione IllegalPositionException. Questa classe è l'unica, fra quelle che estendono BattleFieldElement ad avere un costruttore che prende solo tre argomenti: non viene infatti passata nel costruttore la coordinate y poiché quest'ultima è fissa e non può cambiare.

Casemate

Le istanze della classe Casemate (che estende BattleFieldElement) rappresentano le casematte. Sono ferme nella loro posizione e ce ne possono essere un po' ovunque tranne in quella più in basso riservata al cannoncino (nell'ipotesi si deve sollevare l'eccezione IllegalPositionException).

BattleField

BattleField è la classe che descrive il campo di gioco (lo schermo del videogioco per intendersi) durante il gioco stesso. Sostanzialmente può essere schematizzato come una matrice di n x m elementi, con n e m non definiti a priori. Ogni elemento o sarà vuoto o conterrà un alieno, un cannoncino, uno sparo o una casamatta. In ogni istante un'istanza della classe BattleField fornirà uno snapshot della situazione (configurazione) corrente del campo di gioco.

Esempio di configurazione

La configurazione è codificata in una stringa come segue:

La Figura mostra una possibile configurazione per un campo di gioco di 7 x 10 caselle. In particolare sono presenti 18 elementi: 1 navicella, 1 cannoncino, 3 casematte e 13 alieni. La corrispondente stringa sarà:

7|10|8 1R1 $4 3A1 1A1 $4 2C1 1A2 $4 2A4 $2 1C4A3 $2 1A7 $1 1G8 $

Notare che nell'esempio non ci sono spari in gioco e gli alieni sono tutti trattati allo stesso modo (ad eccezione della navicella rossa).

Attributi

Metodi e costruttori

Attenzione che i vari metodi potrebbero sollevare delle eccezioni queste non sono state specificate nel testo ma dovranno essere gestite cum grano salis.

Eccezioni

Le classi IllegalElementException, IllegalPositionException e IllegalMovementException, da definire in modo opportuno, rappresentano le eccezioni da lanciare (con le modalità indicate) quando si verificano le condizioni di errore descritte nei punti precedenti. Tutte le altre eccezioni previste dall'uso di metodi Java devono filtrare ed essere gestite nel metodo main() anche se non espressamente indicato dalla segnatura dei metodi introdotti in questo documento.

A parte quanto espressamente richiesto, è lasciata piena libertà sull'implementazione delle singole classi e sull'eventuale introduzione di altre classi, a patto di seguire le regole del paradigma ad oggetti ed i principi di buona programmazione.

Non è richiesto e non verrà valutato l'utilizzo di particolari modalità grafiche di visualizzazione: è sufficiente una qualunque modalità di visualizzazione basata sull'uso dei caratteri. È invece espressamente richiesto di non utilizzare package non standard di Java (si possono quindi utilizzare java.util, java.io e così via), con l'unica eccezione del package prog.io incluso nel libro di testo per gestire l'input da tastiera e l'output a video e di rispettare l'interfaccia fornita.

Esempio di programma di test ed esecuzione

La classe TestSpaceInvaders riportata di seguito è un esempio di possibile main() contro il quale potrebbe essere provato il vostro programma.

public class TestSpaceInvaders {
  public static void main(String[] args) throws Exception {
	    BattleField bf = new BattleField( "es-in.txt" );
	    System.out.println( "at the beginning :- " + bf.getBattleField() + bf.toString() );
	    bf.move();
	    System.out.println( "1st step :- " + bf.getBattleField() + bf.toString() );
	    bf.move();
	    System.out.println( "2nd step :- " + bf.getBattleField() + bf.toString() );
	    bf.move();
	    System.out.println( "3rd step :- " + bf.getBattleField() + bf.toString() );
	    bf.setBattleFieldElement( 5, 3, new GunShot( 5, 3, bf.rows(), bf.cols() ) );
	    bf.setBattleFieldElement( 2, 1, new AlienShot( 2, 1, bf.rows(), bf.cols() ) );
	    System.out.println( "new entries :- " + bf.getBattleField() + bf.toString() );
	    bf.move();
	    System.out.println( "4th step :- " + bf.getBattleField() + bf.toString() );
	    bf.move();
	    System.out.println( "5th step :- " + bf.getBattleField() + bf.toString() );
	    bf.move();
	    bf.backup( "es-out.txt" );
  }
}
Supponete che il file es-in.txt sia:
5|5|1 1B3 $5 $2 1C2 $5 $2 1A2 $
5|5|1 1R3 $5 $1C1 1C2 $5 $2 1A2 $
7|10|8 1R1 $4 3A1 1A1 $4 2C1 1A2 $4 2A4 $2 1C4A3 $2 1A7 $1 1G8 $
7|10|1 1R6 1R1 $4 3A1 1A1 $4 2C1 1A2 $4 2A4 $2 1C4A3 $2 1A7 $1 1G8 $
L'esecuzione del programma di cui sopra produrrà su standard output quanto segue:
at the beginning :- 7|10|1 1R6 1R1 $4 3A1 1A1 $4 2C1 1A2 $4 2A4 $2 1C4A3 $2 1A7 $1 1G8 $
............
. R      R .
.    AAA A .
.    CC A  .
.    AA    .
.  CAAAA   .
.  A       .
. G        .
............

1st step :- 7|10|1R6 1R2 $3 3A1 1A2 $4 2C1A3 $3 2A5 $3 3A4 $1 1A8 $2 1G7 $
............
.R      R  .
.   AAA A  .
.    CCA   .
.   AA     .
.   AAA    .
. A        .
.  G       .
............

2nd step :- 7|10|6 1R3 $2 3A1 1A3 $4 1C5 $2 2A6 $2 3A5 $1A9 $3 1G6 $
............
.      R   .
.  AAA A   .
.    C     .
.  AA      .
.  AAA     .
.A         .
.   G      .
............

3rd step :- 7|10|5 1R4 $3 3A1 1A2 $4 1C5 $3 2A5 $3 3A4 $1 1A8 $4 1G5 $
............
.     R    .
.   AAA A  .
.    C     .
.   AA     .
.   AAA    .
. A        .
.    G     .
............

new entries :- 7|10|5 1R4 $2 1S3A1 1A2 $4 1C5 $3 2A1s4 $3 3A4 $1 1A8 $4 1G5 $
............
.     R    .
.  SAAA A  .
.    C     .
.   AAs    .
.   AAA    .
. A        .
.    G     .
............

4th step :- 7|10|4 1R5 $4 3A1 1A1 $2 1S1 1C1s4 $4 2A4 $4 3A3 $2 1A7 $5 1G4 $
............
.    R     .
.    AAA A .
.  S Cs    .
.    AA    .
.    AAA   .
.  A       .
.     G    .
............

5th step :- 7|10|3 1R6 $6 2A1 1A$4 1C5 $2 1S2 2A3 $5 3A2 $3 1A6 $6 1G3 $
............
.   R      .
.      AA A.
.    C     .
.  S  AA   .
.     AAA  .
.   A      .
.      G   .
............

Il contenuto del file es-out.txt alla fine sarà:
7|10|2 1R7 $5 2A1 1A1 $4 1C5 $4 2A4 $2 1S1 3A3 $2 1A7 $7 1G2 $