import java.awt.Graphics; import java.awt.event.*; import java.awt.*; import java.applet.Applet; import java.lang.Thread; import java.net.URL; import java.net.MalformedURLException; import java.io.File; public class Solitari extends Applet { Label l; Botonet solaux=new Botonet(); Resolucio solu=new Resolucio(); String moviments="$"; GridLayout layout; BorderLayout lay2; Panel marcador,botons; int movi=0; Panelet bolletes; Choice cskin; GestorBoto gboto; GestorFinestra gfinestra; GestorChoice gchoice; Botonet bsel,baux; Panelet fons[]; Botonet tauler[][]; boolean seleccionat=false; Frame f=new Frame(); Window w=new Window(f); Frame instruccions = new Frame("Instruccions"); String skin="default"; String skins[]; int nskins; URL dir,dirs; String dirskin; int quin=0; Image imatge; int quina=0; boolean solucionant=false; public void init() { // Panel Marcador layout = new GridLayout (1,1); marcador = new Panel (layout); Color col= new Color(211,220,219); marcador.setBackground(col); l = new Label ("Marcador:0", Label.CENTER); marcador.add(l); // Panel Botons + Choice d'aparences baux = new Botonet(); bsel = new Botonet(); tauler = new Botonet[9][9]; fons =new Panelet [50]; gboto=new GestorBoto(); layout= new GridLayout(1,6); layout.setHgap(10); botons = new Panel(layout); col= new Color(211,220,219); botons.setBackground(col); // Afegim el ciinc botons... afegirbotonet(botons, "Comença"); afegirbotonet(botons, "Enrere"); afegirbotonet(botons, "Aleatori"); afegirbotonet(botons, "Instruccions"); afegirbotonet(botons, "Solució"); // I afegim el choice amb tots els skins disponibles // Primer trobam el nom de la carpeta de skins dir=getDocumentBase(); dirskin=dir.getFile(); int darrer=dirskin.length()-1; while(dirskin.charAt(darrer)!='/') darrer--; dirskin=dirskin.substring(0,darrer); dirskin=dirskin+"/skins/"; skins=llista(); //Cercam la llista de possibles skins nskins=java.lang.reflect.Array.getLength(skins); cskin = new Choice(); // I afegim l'item si és un skin for (int i=0; i=3) && (i<=5) && (j!=0) && (j!=8)) || ( (j>=3) && (j<=5) && (i!=0) && (i!=8) )) { // Esta dins del tauler afegirbotonet(bolletes," 0 ","bolleta.gif","bolletas.gif","blanc.gif",i,j); } else { // Posicions fora del tauler. Cream en aquesta posició un boto per que no quedi sense // inicialitzar (ja que s'utilitzen aquestes posicions al mètode gameover). afegirblanc(bolletes); Botonet boto = new Botonet("res"); tauler[i][j]=boto; } } // Llevam la fitxa central tauler[4][4].esborrar(); tauler[4][4].setLabel(""); // Ho colocam tot lay2 = new BorderLayout(); setLayout(lay2); add(marcador, BorderLayout.NORTH); add(botons, BorderLayout.SOUTH); add(bolletes, BorderLayout.CENTER); // I cream l'altra finestra amb les instruccions TextArea ta=new TextArea(" L'objectiu del joc és anar llevant les fitxes del \n tauler fins que només en quedi una.\n Per fer-ho, s'ha de sel·leccionar una fitxa i \n moure-la en sentit horitzontal o vertical,\n passant per damunt d'una altra fitxa, a un \n espai buit.\n Una partida se considerarà perfecta, si \n s'aconseguiex deixar la darrera fitxa a l'espai \n d'enmig.\n \n Tens l'opció de jugar una partida amb les \n fitxes repartides pel tauler de manera \n aleatòria. Per aquesta opció, pitja el botó \n 'Aleatori'.\n \n Si vols veure com es pot resoldre amb totes les \n fitxes, pitja el botó 'Solució'.\n \n Ah!... i tria l'aparença que més t'agradi per \n jugar. Fins i tot, pots crear el teu skin.\n Només has de possar les 4 imatges necessàries \n dins una carpeta al directori de skins. ."); Font fon=new Font("Serif",java.awt.Font.PLAIN,14); ta.setEditable(false); ta.setFont(fon); instruccions.add(ta); instruccions.setLocation(150,150); instruccions.setSize(315,420); instruccions.setResizable(false); gfinestra=new GestorFinestra(); instruccions.addWindowListener(gfinestra); } //J private void afegirblanc(Panel p) { Panelet pa ; p.add(pa = new Panelet (new GridLayout (1,1))); fons[quin] =pa; quin++; pa.setImage("fons.gif"); } //J private String[] llista() { File e=new File(dirskin); String llistat[]=e.list(); return(llistat); } private boolean isskin (String direc) // Verifica que un File de dins la carperta de skins sigui un directori, i contengui les // quatre imatges necessàries per dibuixar el tauler del joc. { String docu=dirskin+direc+"/"; File fit= new File(docu); // Comprova que sigui una carpeta if (!(fit.isDirectory() )) return(false) ; //I que existesquin les 4 imatges String docu2=docu+"bolleta.gif"; fit=new File(docu2); if (!(fit.exists())) return(false) ; docu2=docu+"bolletas.gif"; fit=new File(docu2); if (!(fit.exists())) return(false) ; docu2=docu+"blanc.gif"; fit=new File(docu2); if (!(fit.exists())) return(false) ; docu2=docu+"fons.gif"; fit=new File(docu2); if (!(fit.exists())) return(false) ; // Si ha arribat fins aqui, és perque tot és correcte return(true) ; } //J private void canviarskin(String sk) { String docu=dirskin+sk+"/"; String pr=dir.getProtocol(); String ho=dir.getHost(); int pot=dir.getPort(); try{ dirs=new URL(pr,ho,pot,docu);}catch(MalformedURLException e){} } private void afegirbotonet(Panel p, String c, String ima0, String ima1, String ima2, int i,int j) // Crea i afegeix a un panel un botonet. Inicialitza el botonet amb les tres imatges possibles. // A més, afegim el botonet a la taula. { Botonet boto; p.add(boto = new Botonet(c,ima0, ima1, ima2) ); boto.setName(i+" "+j); tauler[i][j]=boto; boto.addActionListener(gboto); } private void afegirbotonet(Panel p, String c) // Crea i afegeix un botonet a un panel, però en aquest cas sense cap imatge, és a dir, com si // fos un botó usual. { Botonet boto; p.add(boto = new Botonet(c) ); boto.addActionListener(gboto); } private boolean moure(Botonet origen,Botonet desti) // Donats dos botonets, si és possible, realitzam el moviment d'una fitxa d'un botó a l'altre. // El botonet desti, sempre esta buit. { // Per comprovar si es pot realitzar el moviment, hem d'accedir al boto que es troba entre // els botons. Per això, hem de trobar la posició de cada boto. int or=pos(origen); // i*10 + j int des=pos(desti); int mig=(or+des)/2; int migi= mig/10; int migj=mig-migi*10; int dif=Math.abs(or-des); if (origen.getLabel() == " 0 ") // Al botó d'origen ha d'haber una fitxa per moure... { // ... i la diferència de posicions ha de ser de 20 o 2 // i el boto d'enmig ha de tenir una fitxa. if(((dif==20) || (dif==2)) && (tauler[migj][migi].getLabel()==" 0 ")) { // Llevam les fitxes de l'origen i la d'enmig. origen.setLabel(""); origen.esborrar(); tauler[migj][migi].setLabel(""); tauler[migj][migi].esborrar(); // I possam la fitxa al boto de desti, que queda seleccionat. desti.setLabel(" 0 "); desti.seleccionar(); bsel=desti; // Si hem canviat de boto, possam un separador per el moviment, i incrementam // el marcador de moviments. if(seleccionat==true) { moviments="t"+moviments; movi++; l.setText("Marcador:"+movi); seleccionat=false; } // Guardam el moviment que hem fet. moviments=origen.getName()+" "+desti.getName()+" "+moviments; // I miram si després d'aquest moviment s'ha acabat la partida. gameover(); return(true); } } return(false); } //J private boolean desmoure(Botonet origen,Botonet desti) { int or=pos(origen); int des=pos(desti); int mig=(or+des)/2; int migi= mig/10; int migj=mig-migi*10; int dif=Math.abs(or-des); if (origen.getLabel() == "") { if(((dif==20) || (dif==2)) && (tauler[migj][migi].getLabel()=="")) { origen.setLabel(" 0 "); origen.seleccionar(); tauler[migj][migi].setLabel(" 0 "); tauler[migj][migi].normal(); desti.setLabel(""); desti.esborrar(); bsel=origen; moviments=moviments.substring(8); return(true); } } return(false); } //j private int pos(Botonet b) { String nom= b.getName(); int j=nom.charAt(0)-'0'; int i=nom.charAt(2)-'0'; return i*10+j; } private void gameover() // Després de cada moviment, miram si queda qualque moviment possible, fent un recorregut per // la taula de botonets. { TextField tf; int bolles=0; // Per cada fitxa que quedi damunt el tauler, miram si la podem moure en alguna // de les quatre direccions. En cas afirmatiu, sortim. for (int i=0; i<8 ;i++) for (int j=0; j<8 ; j++) { if (tauler[i][j].getLabel() == " 0 ") { bolles++; // Contam les bolles que queden // Cap adalt if ((i>1)&&(tauler[i-1][j].getLabel() == " 0 ")&& (tauler[i-2][j].getLabel() == "")) return; // Cap abaix if ((i<5)&&(tauler[i+1][j].getLabel() == " 0 ")&& (tauler[i+2][j].getLabel() == "")) return; // Cap a l'esquerra if ((j>1)&&(tauler[i][j-1].getLabel() == " 0 ")&& (tauler[i][j-2].getLabel() == "")) return; // Cap a la dreta if ((j<5)&&(tauler[i][j+1].getLabel() == " 0 ")&& (tauler[i][j+2].getLabel() == "")) return; } } // No podem fer cap moviment, però si només queda una fitxa hem guanyat. if (bolles == 1) { // Si la fitxa està a la posició central, és una partida perfecta. if (tauler[4][4].getLabel() == " 0 ") { Dialog d; Frame fr=new Frame(); d = new Dialog(fr,"Has guanyat"); tf = new TextField("Perfecte!"); Font fn=new Font("Serif",java.awt.Font.BOLD,14); tf.setEditable(false); tf.setFont(fn); d.add(tf); d.addWindowListener(gfinestra); d.setSize(130,149); d.setLocation(200,200); d.setEnabled(false); d.setModal(true); d.setVisible(true); } else { Dialog d; Frame fr=new Frame(); d = new Dialog(f,"Has guanyat"); tf = new TextField("Així se fa!"); Font fn=new Font("Serif",java.awt.Font.BOLD,14); tf.setEditable(false); tf.setFont(fn); d.add(tf); d.addWindowListener(gfinestra); d.setSize(130,149); d.setLocation(200,200); d.setEnabled(false); d.setModal(true); d.setVisible(true); } } else // Queden més d'una fitxa, i no podem fer cap moviment més, és a dir, hem perdut. { Dialog d; Frame fr=new Frame(); d = new Dialog(f,"Has Perdut"); tf = new TextField("Torna a provar!"); Font fn=new Font("Serif",java.awt.Font.BOLD,14); tf.setEditable(false); tf.setFont(fn); d.add(tf); d.addWindowListener(gfinestra); d.setSize(130,149); d.setLocation(200,200); d.setEnabled(false); d.setModal(true); d.setVisible(true); } } class GestorChoice implements ItemListener // Amb aquesta classe escoltam el Choice de les aparences. { public void itemStateChanged(ItemEvent e) // Unicament hem d'implementar aquest mètode. // En aquest cas, canviarem el URL on es troben les imatges, // i canviam les imatges de cada boto o panel. { Choice c = (Choice) e.getSource(); canviarskin(c.getSelectedItem()); Image imatge= getImage (dirs, "fons.gif"); for (int j=0; j<8 ; j++) for (int i=0; i<8 ; i++) { if (((i>=3) && (i<=5) && (j!=0) && (j!=8)) || ( (j>=3) && (j<=5) && (i!=0) && (i!=8) )) { tauler[i][j].setImage("bolleta.gif","bolletas.gif","blanc.gif"); } } for(int h=0;h=3) && (i<=5) && (j!=0) && (j!=8)) || ( (j>=3) && (j<=5) && (i!=0) && (i!=8) )) { tauler[i][j].normal(); tauler[i][j].setLabel(" 0 "); } } tauler[4][4].esborrar(); tauler[4][4].setLabel(""); } if (b.getLabel() =="Aleatori") { movi=0; l.setText("Marcador:"+movi); moviments="$"; bsel=b; int numbolles=0; for (int j=0; j<8 ; j++) for (int i=0; i<8 ; i++) { if (((i>=3) && (i<=5) && (j!=0) && (j!=8)) || ( (j>=3) && (j<=5) && (i!=0) && (i!=8) )) { if((numbolles<33)&&(java.lang.Math.random()>0.5)) { tauler[i][j].normal(); tauler[i][j].setLabel(" 0 "); numbolles++; } else { tauler[i][j].esborrar(); tauler[i][j].setLabel(""); } } } } if (b.getLabel() =="Instruccions") { instruccions.setVisible(true); } if (b.getLabel() =="Enrere") { if(moviments.length()>2) { if (baux.getLabel()==" 0 ") baux.normal(); bsel.normal(); int i0=moviments.charAt(0)-'0'; int j0=moviments.charAt(2)-'0'; int i1=moviments.charAt(4)-'0'; int j1=moviments.charAt(6)-'0'; desmoure(tauler[i0][j0],tauler[i1][j1]); baux=tauler[i0][j0]; if(moviments.charAt(0)=='t') { movi--; l.setText("Marcador:"+movi); seleccionat=true; moviments=moviments.substring(1); } } } if (b.getLabel() =="Solució") { bsel=b; movi=0; l.setText("Marcador:"+movi); for (int j=0; j<8 ; j++) for (int i=0; i<8 ; i++) { if (((i>=3) && (i<=5) && (j!=0) && (j!=8)) || ( (j>=3) && (j<=5) && (i!=0) && (i!=8) )) { tauler[i][j].normal(); tauler[i][j].setLabel(" 0 "); } } tauler[4][4].esborrar(); tauler[4][4].setLabel(""); b.setLabel("Atura"); solaux=b; solu= new Resolucio(); solu.start(); } if (b.getLabel() == " 0 ") { b.seleccionar(); bsel=b; seleccionat=true; } else { moure(bsel,b); } } } } public class Botonet extends Button // Aquesta classe Botonet és un boto usual al que li hem afegit tres imatges, que // es mostraran segons l'estat: 0 - normal; 1 - seleccionat; 2 - esborrat. // També s'utilitzen Botonets sense imatges, així que utilitzam la variable dibuixa per // decidir que se fa als mètodes de dibuix. { Image fit[] = new Image[3]; private boolean dibuixa = true; int estat=0; public Botonet(String c,String ima0,String ima1, String ima2) // Constructor { super(c); // Crida al constructor de la classe Button fit[0] = getImage (dirs, ima0); fit[1] = getImage (dirs, ima1); fit[2] = getImage (dirs, ima2); Color col= new Color(255,255,255); setBackground(col); setForeground(col); } public Botonet(String c) // Constructor d'un Botonet sense imatges. { super(c); dibuixa=false; } public Botonet() { super(); } public void seleccionar() // Canviam l'estat del botó a seleccionat (1), i com a conseqüència es canvia la imatge, { estat=1; this.repaint(); } public void esborrar() // Canviam l'estat del botó a esborrat (2), és a dir, llevam la fitxa. { estat=2; this.repaint(); } public void normal() // Canvia l'estat del botó a normal (0). Serveix per possar una fitxa en aquest botó, // o per deseleccionar la fitxa. { estat=0; this.repaint(); } public void paint(Graphics g) // En el cas d'un botó amb imatges, el dibuixa segons l'estat en que es trobi. { if(dibuixa) { int h = this.getHeight(); int w = this.getWidth(); g.drawImage(fit[estat], 0, 0, w, h,this); } } public void setImage(String ima0,String ima1, String ima2) // Canvia les imatges del botó per aquestes noves tres. { fit[0] =getImage (dirs, ima0); fit[1] =getImage (dirs, ima1); fit[2] =getImage (dirs, ima2); this.repaint(); } } //j public class Panelet extends Panel { Image imatge; boolean pinta=false; public Panelet() { super(); } public Panelet(LayoutManager layout) { super(layout); } public void setImage(String im) { imatge=getImage (dirs, im); pinta=true; this.repaint(); } public void paint(Graphics g) { if(pinta) { int h = this.getHeight(); int w = this.getWidth(); g.drawImage(imatge, 0, 0, w, h,this); } } } class Resolucio extends Thread // Amb aquesta classe podem fer que les fitxes es moguin pel tauler i es jugui la partida // automàticament, acabant guanyant de manera perfecta. { public void run() { try { solucionant=true; // Serveix per desactivar tots els botons excepte el d'aturar mentre // s'executa el Thread. // Per a cada moviment, seleccionam la fitxa a moure, i aturam un segon. // Igualment, en acabar el moviment, espera un segon per canviar de fitxa. tauler[6][4].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[6][4],tauler[4][4]); sleep(1000); tauler[4][4].normal(); tauler[5][2].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[5][2],tauler[5][4]); sleep(1000); tauler[5][4].normal(); tauler[7][3].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[7][3],tauler[5][3]); sleep(1000); tauler[5][3].normal(); tauler[4][3].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[4][3],tauler[6][3]); sleep(1000); tauler[6][3].normal(); tauler[2][3].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[2][3],tauler[4][3]); sleep(1000); tauler[4][3].normal(); tauler[3][1].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[3][1],tauler[3][3]); sleep(1000); tauler[3][3].normal(); tauler[3][4].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[3][4],tauler[3][2]); sleep(1000); tauler[3][2].normal(); // En el cas d'utilitzar la mateixa fitxa per fer més d'un moviment, l'espera // entre moviment i moviment és més curta. tauler[5][1].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[5][1],tauler[3][1]); sleep(200); moure(tauler[3][1],tauler[3][3]); sleep(1000); tauler[3][3].normal(); tauler[5][5].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[5][5],tauler[5][3]); sleep(1000); tauler[5][3].normal(); tauler[5][7].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[5][7],tauler[5][5]); sleep(1000); tauler[5][5].normal(); tauler[3][6].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[3][6],tauler[3][4]); sleep(200); moure(tauler[3][4],tauler[3][2]); sleep(200); moure(tauler[3][2],tauler[5][2]); sleep(200); moure(tauler[5][2],tauler[5][4]); sleep(200); moure(tauler[5][4],tauler[5][6]); sleep(1000); tauler[5][6].normal(); tauler[7][5].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[7][5],tauler[7][3]); sleep(200); moure(tauler[7][3],tauler[5][3]); sleep(200); moure(tauler[5][3],tauler[3][3]); sleep(1000); tauler[3][3].normal(); tauler[3][7].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[3][7],tauler[5][7]); sleep(200); moure(tauler[5][7],tauler[5][5]); sleep(1000); tauler[5][5].normal(); tauler[1][5].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[1][5],tauler[3][5]); sleep(1000); tauler[3][5].normal(); tauler[4][5].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[4][5],tauler[2][5]); sleep(1000); tauler[2][5].normal(); tauler[1][3].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[1][3],tauler[1][5]); sleep(200); moure(tauler[1][5],tauler[3][5]); sleep(1000); tauler[3][5].normal(); tauler[6][5].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[6][5],tauler[4][5]); sleep(200); moure(tauler[4][5],tauler[2][5]); sleep(200); moure(tauler[2][5],tauler[2][3]); sleep(200); moure(tauler[2][3],tauler[4][3]); sleep(200); moure(tauler[4][3],tauler[4][5]); sleep(1000); tauler[4][5].normal(); tauler[4][6].seleccionar(); seleccionat=true; sleep(1000); moure(tauler[4][6],tauler[4][4]); sleep(1000); // Tornam a possar el Label solució al botó que ara posava Atura. solaux.setLabel("Solució"); solucionant=false; }catch(InterruptedException e) { return; } } } }