|
|
|
|
La Vida al Món |
|
|
|
|
|
Finalment, aquesta és
la pràctica en la qual, al final, el Joc
ja es juga... Les classes i mètodes ja quedaran complets i, almenys
pas a pas, ja podràs veure l'evolució dels Essers
en el mon del Joc
de la Vida. |
|
|
|
|
|
Posar i treure éssers
del món: |
|
|
|
|
|
|
A la planificació de la
pràctica 1 està
previst que l'usuari de l'aplicació del Joc
de la Vida pugui posar i treure éssers del món
on vulgui i quan vulgui amb un simple clic de ratolí. Això
implica que el MonVisible ha de ser sensible als
clics del ratolí i que, per tant, cal afegir-li un MouseListener
que els capti. De les dues possibilitats, fer que la classe MonVisible
sigui ella mateixa el listener dels esdeveniments MouseEvent,
o bé, crear un objecte java.awt.event.MouseAdapter
per a aquesta finalitat (recorda el que se'n diu d'això a la pràctica
7 del mòdul 6) escolliràs la segona. |
|
|
|
|
 |
Som-hi: al mètode constructor
de la classe MonVisible afegeix-li això
(no t'oblidis d'importar les classes java.awt.event.MouseAdapter
i java.awt.event.MouseEvent!): |
|
|
|
|
 |
/**
* Mètode constructor per a objectes
de la classe MonVisible.
* @param finestra La Finestra que conté
aquest MonVisible
*/
public MonVisible(Finestra finestra) { //
constructor
this.finestra=finestra;
setPreferredSize(finestra.getDimensionsMonVisible());
MouseAdapter mouseAdapter=new
MouseAdapter() {
public
void mouseClicked(MouseEvent e) {
//
Feina per fer: definir què passa quan hi ha
//
un click.
}
};
addMouseListener(mouseAdapter);
} |
|
|
|
|
|
|
Ara el MonVisible
ja és sensible als clics del ratolí i ja només cal
establir què passa quan hi ha un click sobre ell, és a dir,
només cal posar el contingut del mètode public
void mouseClicked(MouseEvent e) de l'objecte mouseAdapter
que s'acaba de crear. |
|
|
|
|
|
Com que el mètode public
void mouseClicked(MouseEvent e) de l'objecte mouseAdapter
ha estat definit just en el moment de crear aquest objecte, en escriure
el codi d'aquest mètode, les variables de la classe MonVisible
no són directament manipulables i cal obtenir-les resseguint el camí
pel qual vé el MouseEvent e: |
|
|
|
|
|
- Determinació de l'objecte MonVisible
en el qual s'ha originat l'esdeveniment. El mètode a fer servir
és el getComponent() de la classe java.awt.event.ComponentEvent
(de la qual és filla la classe MouseEvent),
que et tornarà l'objecte java.awt.Component
(la classe MonVisible és filla de JPanel
i, per tant, de java.awt.Component):
MonVisible
monVisible=(MonVisible)e.getComponent(); |
El casting per passar el java.awt.Component
que ens torna el mètode a MonVisible
és imprescindible!
- En quina Finestra està
aquest MonVisible?
Finestra
finestra=monVisible.finestra; |
- Quin Mon mou les coses en aquest
MonVisible?
Mon
mon=finestra.getMon(); |
- A quines coordenades s'ha esdevingut el MouseEvent
e?
int
posX=e.getX();
int posY=e.getY(); |
- A quina cel·la del mon
corresponen aquestes coordenades?
int
ampladaEssers=Constants.dimensionsEsser.width;
int alturaEssers=Constants.dimensionsEsser.height;
int x=posX/ampladaEssers;
int y=posY/alturaEssers; |
|
|
|
Ara ja està tot a punt:
primer fes que canviï l'estat de l'Esser
que és just en aquesta posició del mon:
|
|
|
|
|
|
mon.canviaEstatViuOMort(x,y); |
|
|
|
|
|
|
i, després, repinta el
MonVisible per tal que es vegi el canvi: |
|
|
|
|
|
|
|
|
|
|
|
El codi que omple el mètode
public void mouseClicked(MouseEvent e) de l'objecte
mouseAdapter és, doncs: |
|
|
|
|
 |
/**
* Mètode constructor per a objectes
de la classe MonVisible.
* @param finestra La Finestra que conté
aquest MonVisible
*/
public MonVisible(Finestra finestra) { //
constructor
this.finestra=finestra;
setPreferredSize(finestra.getDimensionsMonVisible());
MouseAdapter mouseAdapter=new
MouseAdapter() {
public
void mouseClicked(MouseEvent e) {
MonVisible
monVisible=(MonVisible)e.getComponent();
Finestra
finestra=monVisible.finestra;
Mon
mon=finestra.getMon();
int
posX=e.getX();
int
posY=e.getY();
int
ampladaEssers=Constants.dimensionsEsser.width;
int
alturaEssers=Constants.dimensionsEsser.height;
int
x=posX/ampladaEssers;
int
y=posY/alturaEssers;
mon.canviaEstatViuOMort(x,y);
monVisible.repaint();
}
};
addMouseListener(mouseAdapter);
} |
|
|
|
|
|
 |
Ja pots compilar, crear un objecte
Finestra i, a clic de ratolí, posar i treure
éssers del món: |
|
|
|
|
|
|
|
|
|
|
|
L'extermini: |
|
|
|
|
|
|
Ara estàs en dispòsició
de fer que el botó Extermini faci el que ha de fer i, a més,
veure-ho: es tracta, doncs, d'escriure-li codi al mètode public
void actionPerformed(ActionEvent evt) de l'objecte ActionListener
construït en crear el JButton "Extermini".
És ben senzill: a la classe Mon hi tens
el mètode public void ompleMon (), que
omple el món d'Essers morts; el crides
i després repintes el monVisible
per veure'n els canvis: |
|
|
|
|
 |
// Col·locació
del botó "Extermini". No cal crear-lo abans
afegeixBoto(panelBotons,"Extermini",
new
ActionListener() {
public
void actionPerformed(ActionEvent evt) {
mon.ompleMon();
monVisible.repaint();
}
}); |
|
|
|
|
|
|
Compilació i prova: |
|
|
|
|
|
|
|
|
|
|
|
Pas a pas... |
|
|
|
|
|
|
Arriba el moment de fer que el
joc jugui! De moment, només pas a pas: el procés
continu queda per a la pràctica
següent. |
|
|
|
|
|
Comença per fer actiu
el botó "Pas a pas". De la mateixa manera que amb
el botó "Extermini", cal omplir amb codi el mètode
public void mouseClicked(MouseEvent e) de l'objecte
mouseAdapter del JButton
"Pas a pas". El codi és ben semblant: |
|
|
|
|
 |
// Col·locació
del botó "Pas a pas"
botoPasAPas=new JButton(); // Creem el botó
afegeixBoto(botoPasAPas,panelBotons,"Pas
a pas",
new
ActionListener() {
public
void actionPerformed(ActionEvent evt) {
mon.pas();
monVisible.repaint();
}
}); |
|
|
|
|
|
|
Naturalment, això implica
que el mètode public void pas () ha de
ser present a la classe Mon (no hi era encara!).
Posa'l, encara que no tingui codi a diintre: |
|
|
|
|
 |
/**
* Fa un pas en l'evolució d'aquest
Mon.
*/
public void pas () {
//
Feina per fer: definir què fa
} |
|
|
|
|
|
|
i ara ja et pots oblidar del
botó "Pas a pas" (ja funciona, però no fa res perquè
el mètode al qual crida, public void pas (),
no fa res!) i concentrar-te en jugar el joc amb el codi d'aquest
mètode. |
|
|
|
|
|
Que, a cada pas, un Esser neixi, sobrevisqui
o mori depèn del nombre d'Essers veïns
vius que té en un cert moment. Això implica que necessites
un mètode que et compti el nombre de veïns vius que un cert
Esser, el de la posició x,y,
té. El dibuix següent mostra les posicions d'aquests vuit
veïns:
|
|
|
|
|
|
|
|
|
|
|
|
i el codi del mètode,en
principi, és aquest: |
|
|
|
|
 |
/**
* Recompte dels veïns vius de
l'Esser a la posició x,y.
*
@param x la columna d'elMon en la qual és aquest Esser
* @param y la fila d'elMon en la qual
és aquest Esser
*
@return en nombre de veïns
vius d'aquest Esser
*/
private int quantsVeinsVius (int
x,int y) {
int quants=0;
if
(elMon[x-1][y-1].getEsViu()) {
quants++;
}
if
(elMon[x][y-1].getEsViu()) {
quants++;
}
if
(elMon[x+1][y-1].getEsViu()) {
quants++;
}
if
(elMon[x-1][y].getEsViu()) {
quants++;
}
if
(elMon[x+1][y].getEsViu()) {
quants++;
}
if
(elMon[x-1][y+1].getEsViu()) {
quants++;
}
if
(elMon[x][y+1].getEsViu()) {
quants++;
}
if
(elMon[x+1][y+1].getEsViu()) {
quants++;
}
return quants;
}
|
|
|
|
|
|
|
Com ja veus, es tracta de preguntar
a cadascun dels veins de l'Esser a la posició
x,y si són vius o morts i, en cas que un
d'ells sigui viu, incrementar en 1 la variable quants,
que és la que retorna el mètode. |
|
|
|
|
|
Hi ha, però un inconvenient:
què passa quan el mètode pregunta pels veïns a l'esquerra
d'un Esser que és a la columna 0?
què passa quan el mètode pregunta pels veïns a la dreta
d'un Esser que és a la columna dimensionsMon.width-1
(l'última de la dreta)? què passa quan el mètode pregunta
pels veïns de sobre d'un Esser que és
a la fila 0? què passa quan el mètode
pregunta pels veïns de sota d'un Esser que
és a la columna dimensionsMon.height-1
(la de baix de tot)? La resposta és simple: estaríem demanant
per Essers que no són a la matriu elMon
i el sistema treuria una excepció ArrayIndexOutOfBoundsException
("Excepció d'Index de la Matriu Fora de Límits").
Què fer? Hi ha diverses solucions per a això i te'n proposem
una: |
|
|
|
|
|
Enlloc de demanar per l'Esser
a la posició p,q, es tracta de demanar
per l'Esser a la posicio p%dimensionsMon.width,q%dimensionsMon.height.
Amb això s'aconsegueix que, si, per exemple, p
sobrepassa el límit de la dreta, és a dir, si p>dimensionsMon.width,
es va a buscar la posició p%dimensionsMon.width,
la qual és, segur, abans del límit p%dimensionsMon.width.
El mètode quantsVeinsVius() adequadament
reformat quedarà així: |
|
|
|
|
 |
/**
* Recompte dels veïns vius de
l'Esser a la posició x,y.
* @param x la columna d'elMon en la
qual és aquest Esser
* @param y la fila d'elMon en la qual
és aquest Esser
* @return en nombre de veïns
vius d'aquest Esser
*/
private int quantsVeinsVius (int x,int y)
{
int quants=0;
int lDreta=Constants.dimensionsMon.width;
int lBaix=Constants.dimensionsMon.height;
if
(elMon[(x-1+lDreta)%lDreta]
[(y-1+lBaix)%lBaix].getEsViu()) {
quants++;
}
if
(elMon[x%lDreta]
[(y-1+lBaix)%lBaix].getEsViu()) {
quants++;
}
if
(elMon[(x+1)%lDreta]
[(y-1+lBaix)%lBaix].getEsViu()) {
quants++;
}
if
(elMon[(x-1+lDreta)%lDreta]
[y%lBaix].getEsViu()) {
quants++;
}
if
(elMon[(x+1)%lDreta][y%lBaix].getEsViu()) {
quants++;
}
if
(elMon[(x-1+lDreta)%lDreta]
[(y+1)%lBaix].getEsViu()) {
quants++;
}
if
(elMon[x%lDreta][(y+1)%lBaix].getEsViu()) {
quants++;
}
if
(elMon[(x+1)%lDreta][(y+1)%lBaix].getEsViu()) {
quants++;
}
return quants;
}
|
|
|
|
|
|
|
Ara ja toca començar a
posar codi al mètode pas(). Car repassar,
un a un, tots els Essers del mon
i preguntar quants veïns té cadascú. El repàs
es fa per columnes i, a cada columna, per files: |
|
|
|
|
 |
/**
* Fa un pas en l'evolució d'aquest
Mon.
*/
public void pas () {
int lDreta=Constants.dimensionsMon.width;
int lBaix=Constants.dimensionsMon.height;
for
(int x=0;x<lDreta;x++) {
for
(int y=0;y<lBaix;y++) {
int
numVeinsVius=quantsVeinsVius(x,y);
//
Feina per fer: què fer quan ja se sap
//
el nombre de veïns
}
}
} |
|
|
|
|
|
|
És el moment
d'aplicar les regles del Joc
de La Vida:
- Un ésser mort que tingui, exactament, tres
veïns vius, passa a ser un ésser viu.
- Un ésser viu amb 2 o 3 veins vius
roman viu; en cas contrari (cap o un veí viu o
4 o més veïns vius) mor per aïllament
o per superpoblació.
|
|
|
|
|
 |
/**
* Fa un pas en l'evolució d'aquest
Mon.
*/
public void pas () {
int lDreta=Constants.dimensionsMon.width;
int lBaix=Constants.dimensionsMon.height;
for
(int x=0;x<lDreta;x++) {
for
(int y=0;y<lBaix;y++) {
int
numVeinsVius=quantsVeinsVius(x,y);
Esser
esser=elMon[x][y];
boolean
esViu=esser.getEsViu();
if
(esViu) {
if (numVeinsVius==2 ||
numVeinsVius==3)
{
//en
l'estat següent és viu
}
else {
//en
l'estat següent és mort
}
}
else {
if (numVeinsVius==3) {
//en
l'estat següent és viu
}
else {
//en
l'estat següent és mort
}
}
}
}
} |
|
|
|
|
|
|
Ara es planteja el problema de com guardar aquesta informació,
una informació per a cada Esser, fins
que s'hagin repassat tots els Essers del mon:
no pots matar directament un Esser perquè
això falsejaria la informació quant al nombre de veïns
dels Essers que li són veïns! Els
canvis efectius d'estat no es poden fer fins que hagis repassat tots els
Essers del mon!
|
|
|
|
|
|
Hi ha diverses solucions a això.
Una te la proposem ara mateix i una altra en un exercici més
avall. |
|
|
|
|
|
El que et proposem és
que defineixis una matriu d'Essers, de les mateixes
dimensions de la matriu elMon, i hi guardis els
Essers en el seus estats futurs: |
|
|
|
|
 |
/**
* Fa un pas en l'evolució d'aquest
Mon.
*/
public void pas () {
int lDreta=Constants.dimensionsMon.width;
int lBaix=Constants.dimensionsMon.height;
Esser[][] elMonProvisional=new
Esser[lDreta][lBaix];
for
(int x=0;x<lDreta;x++) {
for
(int y=0;y<lBaix;y++) {
int
numVeinsVius=quantsVeinsVius(x,y);
Esser
esser=elMon[x][y];
boolean
esViu=esser.getEsViu();
if
(esViu) {
if (numVeinsVius==2 ||
numVeinsVius==3)
{
elMonProvisional[x][y]=
new
Esser(true);
}
else {
elMonProvisional[x][y]=
new
Esser(false);
}
}
else {
if (numVeinsVius==3) {
elMonProvisional[x][y]=
new
Esser(true);
}
else {
elMonProvisional[x][y]=
new
Esser(false);
}
}
}
}
//
Ara només falta passar aquesta informació a elMon
} |
|
|
|
|
|
|
I, finalment, cal copiar la matriu
elMonProvisional sobre la matriu elMon,
i el mon ja és en un nou estat! |
|
|
|
|
 |
/**
* Fa un pas en l'evolució d'aquest
Mon.
*/
public void pas () {
int lDreta=Constants.dimensionsMon.width;
int lBaix=Constants.dimensionsMon.height;
Esser[][] elMonProvisional=new
Esser[lDreta][lBaix];
for
(int x=0;x<lDreta;x++) {
for
(int y=0;y<lBaix;y++) {
int
numVeinsVius=quantsVeinsVius(x,y);
Esser
esser=elMon[x][y];
boolean
esViu=esser.getEsViu();
if
(esViu) {
if (numVeinsVius==2 ||
numVeinsVius==3)
{
elMonProvisional[x][y]=
new
Esser(true);
}
else {
elMonProvisional[x][y]=
new
Esser(false);
}
}
else {
if (numVeinsVius==3) {
elMonProvisional[x][y]=
new
Esser(true);
}
else {
elMonProvisional[x][y]=
new
Esser(false);
}
}
}
}
for
(int x=0;x<lDreta;x++) {
for
(int y=0;y<lBaix;y++) {
elMon[x][y]=elMonProvisional[x][y];
}
}
} |
|
|
|
|
|
|
Compila, crea un objecte Finestra i...
|
|
|
|
|
|
el
Joc De La Vida (pas
a pas)
ja funcionaaaaaaa!!!!!!! 
|
|
|
|
|
 |
Fer
les coses d'una altra manera: un exercici |
|
|
|
|
|
|
Potser sigui massa aparatós
haver de construir tota una matriu elMonProvisional
només per guardar-hi l'estat futur dels Essers
del mon... Hi ha una altra tècnica que consisteix en que siguin els
propis Essers els qui guardin aquesta informació
(la seva pròpia sentència!). Comença per afegir a la
classe Esser la variable de classe boolean
estaraViu i un mètode setter i un altre mètode
getter per a aquesta variable: |
|
|
|
|
 |
import
java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Color;
/**
* La classe Esser representa un ésser dels que poblen
el món
* del "Joc de la Vida".
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Esser {
/**
* L'estat de viu o mort d'aquest Esser.
*/
private boolean esViu=false;
/**
* L'estat futur de viu o mort d'aquest
Esser.
*/
private boolean
estaraViu=false;
/**
* Mètode constructor per a objectes
de la classe Esser.
* @param esViu l'estat de viu o mort d'aquest
Esser.
*/
public Esser(boolean esViu) { // constructor
this.esViu=esViu;
}
/**
* Obtenció de l'estat de viu o mort
d'aquest Esser.
*/
public boolean getEsViu () {
return esViu;
}
/**
* Determinació de l'estat de viu
o mort d'aquest Esser.
* @param esViu l'estat de viu o mort d'aquest
Esser que
* es vol determinar.
*/
public void setEsViu (boolean esViu) {
this.esViu=esViu;
}
/**
* Obtenció de l'estat futur
de viu o mort d'aquest Esser.
*/
public boolean
getEstaraViu () {
return estaraViu;
}
/**
*
Determinació de l'estat de viu o mort d'aquest Esser.
*
@param esViu l'estat de viu o mort d'aquest Esser que
*
es vol determinar.
*/
public void setEstaraViu
(boolean estaraViu) {
this.estaraViu=estaraViu;
}
/**
* Mètode paint per a aquest Esser.
* @param dimensions les dimensions en píxels
d'aquest Esser.
* @param posicio la posició en píxels
d'aquest Esser.
* @param un objecte Graphics2D.
*/
public void paint (Dimension dimensions,
Dimension posicio,
Graphics2D g) {
if (esViu) { //
Només pintar-lo si és viu
//
Color vermell viu
g.setColor(Color.RED.brighter());
//
Omplir un cercle una mica més petit
g.fillOval(posicio.width+1,posicio.height+1,
dimensions.width-2,dimensions.height-2);
//
Color vermell fosc
g.setColor(Color.RED.darker().darker().darker());
//
Envoltar-lo amb una circumferència
g.drawOval(posicio.width+1,posicio.height+1,
dimensions.width-2,dimensions.height-2);
}
}
}
|
|
|
|
|
|
|
Ara modifica
el mètode pas() de la classe Mon |
|
|
|
|
 |
/**
* Fa un pas en l'evolució d'aquest
Mon.
*/
public void pas () {
int lDreta=Constants.dimensionsMon.width;
int lBaix=Constants.dimensionsMon.height;
Esser[][] elMonProvisional=new
Esser[lDreta][lBaix];
for
(int x=0;x<lDreta;x++) {
for
(int y=0;y<lBaix;y++) {
int
numVeinsVius=quantsVeinsVius(x,y);
Esser
esser=elMon[x][y];
boolean
esViu=esser.getEsViu();
if
(esViu) {
if (numVeinsVius==2 ||
numVeinsVius==3)
{
//
posar nou codi
}
else {
//
posar nou codi
}
}
else {
if (numVeinsVius==3) {
//
posar nou codi
}
else {
//
posar nou codi
}
}
}
}
//
posar nou codi
} |
|
|
|
|
|
|
i, sense definir cap matriu elMonProvisional
o semblant, aconsegueix que tot funcioni bé... |
|
<